diff --git a/.gitignore b/.gitignore index e94cb6340..6677091c7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ Sound/XRSound/**/Release-with-OrbiterRelease/ .kdev/ [Bb]uild/ +# CLion-specific files +.idea + Extern/irrKlang Doc/**/*.log diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f1c284ac..3a09a65dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.19) project (Orbiter VERSION 21.7.24) set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) #Add /permissive if using C++20 or higher @@ -83,7 +83,7 @@ set(ORBITER_INSTALL_DOC_DIR ${ORBITER_INSTALL_ROOT_DIR}/Doc) set(ORBITER_INSTALL_UTILS_DIR ${ORBITER_INSTALL_ROOT_DIR}/Utils) set(ORBITER_INSTALL_SDK_DIR ${ORBITER_INSTALL_ROOT_DIR}/Orbitersdk) -set(ORBITER_SDK_LIB $) +set(ORBITER_SDK_LIB $ SDL3::SDL3) set(ORBITER_DLGCTRL_LIB $) set(LUAINTERPRETER_LIB $) set(GDICLIENT_LIB $) diff --git a/Doc/Orbiter User Manual/CREDITS.tex b/Doc/Orbiter User Manual/CREDITS.tex index eb1424183..cdfba6051 100644 --- a/Doc/Orbiter User Manual/CREDITS.tex +++ b/Doc/Orbiter User Manual/CREDITS.tex @@ -32,6 +32,11 @@ \subsection{Libraries, code, data, algorithms} Jean-loup Gailly \href{mailto:jloup@gzip.org}{jloup@gzip.org}\\ Mark Adler \href{mailto:madler@alumni.caltech.edu}{madler@alumni.caltech.edu}\\ \\ +\textbf{Font Awesome}\\ +Icons font\\ +Fonticons, Inc. \url{https://fontawesome.com}\\ +CC BY 4.0 License \url{https://creativecommons.org/licenses/by/4.0/}\\ +\\ \textbf{VSOP87}\\ Planetary perturbation terms for Mercury to Neptune\\ Bureau des Longitudes, CNRS URA 707\\ diff --git a/Extern/CMakeLists.txt b/Extern/CMakeLists.txt index a8690b14e..da77e26d9 100644 --- a/Extern/CMakeLists.txt +++ b/Extern/CMakeLists.txt @@ -6,6 +6,8 @@ endif(NOT DEFINED ORBITER_BINARY_ROOT_DIR) add_subdirectory(Lua) add_subdirectory(zlib) +add_subdirectory(imgui) +add_subdirectory(SDL3) ## LFS add_library(lfs SHARED luafilesystem/src/lfs.c) diff --git a/Extern/SDL3/CMakeLists.txt b/Extern/SDL3/CMakeLists.txt new file mode 100644 index 000000000..89615a043 --- /dev/null +++ b/Extern/SDL3/CMakeLists.txt @@ -0,0 +1,28 @@ +project(SDL3) + +Include(FetchContent) + +FetchContent_Declare( + SDL3 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-3.2.4 +) +FetchContent_MakeAvailable(SDL3) + +set_target_properties(SDL3-shared PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${ORBITER_BINARY_ROOT_DIR} + LIBRARY_OUTPUT_DIRECTORY ${ORBITER_BINARY_ROOT_DIR} + RUNTIME_OUTPUT_DIRECTORY ${ORBITER_BINARY_ROOT_DIR} +) + +set_target_properties(SDL3_Headers PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${ORBITER_INSTALL_SDK_DIR}/include" + LIBRARY_OUTPUT_DIRECTORY "${ORBITER_INSTALL_SDK_DIR}/include" + RUNTIME_OUTPUT_DIRECTORY "${ORBITER_INSTALL_SDK_DIR}/include" +) + +file(GLOB SDL3_HEADERS "${SDL3_SOURCE_DIR}/include/SDL3/*.h" "${SDL3_BINARY_DIR}/include-revision/SDL3/*.h") +file(COPY ${SDL3_HEADERS} DESTINATION "${ORBITER_BINARY_SDK_DIR}/include/SDL3") + +install(TARGETS SDL3_Headers RUNTIME DESTINATION "${ORBITER_INSTALL_SDK_DIR}/include") +install(TARGETS SDL3-shared RUNTIME DESTINATION ${ORBITER_INSTALL_ROOT_DIR}) \ No newline at end of file diff --git a/Extern/imgui/CMakeLists.txt b/Extern/imgui/CMakeLists.txt new file mode 100644 index 000000000..84b5b14c0 --- /dev/null +++ b/Extern/imgui/CMakeLists.txt @@ -0,0 +1,27 @@ +project(imgui) + +Include(FetchContent) + +FetchContent_Declare( + imgui + GIT_REPOSITORY https://github.com/ocornut/imgui.git + GIT_TAG v1.91.7-docking + PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/imconfig.h . +# UPDATE_DISCONNECTED 1 +) +FetchContent_MakeAvailable(imgui) + +install(FILES ${imgui_SOURCE_DIR}/misc/fonts/Roboto-Medium.ttf fa-solid-900.ttf ${imgui_SOURCE_DIR}/misc/fonts/Cousine-Regular.ttf + DESTINATION ${ORBITER_INSTALL_ROOT_DIR} +) +file(COPY ${imgui_SOURCE_DIR}/misc/fonts/Roboto-Medium.ttf fa-solid-900.ttf ${imgui_SOURCE_DIR}/misc/fonts/Cousine-Regular.ttf DESTINATION ${ORBITER_BINARY_ROOT_DIR}) + +install(FILES ${imgui_SOURCE_DIR}/imgui.h ${imgui_SOURCE_DIR}/imconfig.h + DESTINATION ${ORBITER_INSTALL_SDK_DIR}/include +) + +add_library(imgui INTERFACE) + +set(IMGUI_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) +file(COPY ${imgui_SOURCE_DIR}/imgui.h ${imgui_SOURCE_DIR}/imconfig.h DESTINATION ${IMGUI_INCLUDE_DIR}) +target_include_directories(imgui INTERFACE ${IMGUI_INCLUDE_DIR}) diff --git a/Extern/imgui/License-FA.txt b/Extern/imgui/License-FA.txt new file mode 100644 index 000000000..aa78d8533 --- /dev/null +++ b/Extern/imgui/License-FA.txt @@ -0,0 +1,165 @@ +Fonticons, Inc. (https://fontawesome.com) + +-------------------------------------------------------------------------------- + +Font Awesome Free License + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +-------------------------------------------------------------------------------- + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) + +The Font Awesome Free download is licensed under a Creative Commons +Attribution 4.0 International License and applies to all icons packaged +as SVG and JS file types. + +-------------------------------------------------------------------------------- + +# Fonts: SIL OFL 1.1 License + +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +Copyright (c) 2023 Fonticons, Inc. (https://fontawesome.com) +with Reserved Font Name: "Font Awesome". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE +Version 1.1 - 26 February 2007 + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting — in part or in whole — any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +-------------------------------------------------------------------------------- + +# Code: MIT License (https://opensource.org/licenses/MIT) + +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +Copyright 2023 Fonticons, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + +# Attribution + +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +-------------------------------------------------------------------------------- + +# Brand Icons + +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/Extern/imgui/fa-solid-900.ttf b/Extern/imgui/fa-solid-900.ttf new file mode 100644 index 000000000..a0414182d Binary files /dev/null and b/Extern/imgui/fa-solid-900.ttf differ diff --git a/Extern/imgui/imconfig.h b/Extern/imgui/imconfig.h new file mode 100644 index 000000000..f923f8ccc --- /dev/null +++ b/Extern/imgui/imconfig.h @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// DEAR IMGUI COMPILE-TIME OPTIONS +// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. +// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. +//----------------------------------------------------------------------------- +// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) +// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. +//----------------------------------------------------------------------------- +// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp +// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. +// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. +// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using. +//----------------------------------------------------------------------------- + +#pragma once + +//---- Define assertion handler. Defaults to calling assert(). +// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. +//#define IM_ASSERT(_EXPR) MyAssert(_EXPR) +//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts + +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows +// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. +// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() +// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. +//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export +//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import +//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden + +//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. +//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +//---- Disable all of Dear ImGui or don't implement standard windows/tools. +// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. +//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. +//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. +//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty. + +//---- Don't implement some functions to reduce linkage requirements. +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) +//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) +//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). +//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). +//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). +//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) +//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. +//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) +//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. +//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). +//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert. +//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available + +//---- Enable Test Engine / Automation features. +//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details. + +//---- Include imgui_user.h at the end of imgui.h as a convenience +// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. +//#define IMGUI_INCLUDE_IMGUI_USER_H +//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h" + +//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support. +//#define IMGUI_USE_BGRA_PACKED_COLOR + +//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate. +//#define IMGUI_USE_LEGACY_CRC32_ADLER + +//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) +//#define IMGUI_USE_WCHAR32 + +//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version +// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. +//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" +//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" +//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined. +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined. + +//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) +// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. +//#define IMGUI_USE_STB_SPRINTF + +//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) +// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. +//#define IMGUI_ENABLE_FREETYPE + +//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT) +// Only works in combination with IMGUI_ENABLE_FREETYPE. +// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg. +// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions. +// - Both require headers to be available in the include path + program to be linked with the library code (not provided). +// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement) +//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG +//#define IMGUI_ENABLE_FREETYPE_LUNASVG + +//---- Use stb_truetype to build and rasterize the font atlas (default) +// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. +//#define IMGUI_ENABLE_STB_TRUETYPE + +//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. +// This will be inlined as part of ImVec2 and ImVec4 class declarations. +/* +#define IM_VEC2_CLASS_EXTRA \ + constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \ + operator MyVec2() const { return MyVec2(x,y); } + +#define IM_VEC4_CLASS_EXTRA \ + constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ + operator MyVec4() const { return MyVec4(x,y,z,w); } +*/ +//---- ...Or use Dear ImGui's own very basic math operators. +//#define IMGUI_DEFINE_MATH_OPERATORS + +//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. +// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). +// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. +// Read about ImGuiBackendFlags_RendererHasVtxOffset for details. +//#define ImDrawIdx unsigned int + +//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) +//struct ImDrawList; +//struct ImDrawCmd; +//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); +//#define ImDrawCallback MyImDrawCallback + +//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase) +// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) +//#define IM_DEBUG_BREAK IM_ASSERT(0) +//#define IM_DEBUG_BREAK __debugbreak() + +//---- Debug Tools: Enable slower asserts +//#define IMGUI_DEBUG_PARANOID + +//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files) +/* +namespace ImGui +{ + void MyFunction(const char* name, MyMatrix44* mtx); +} +*/ + +// Orbiter specific : +// The ImGuiContext is owned by the main exe, libraries are importing it. +// The ImGui code is stored in the Orbiter SDK so that modules can use it. +struct ImGuiContext; + +#ifdef EXPORT_IMGUI_CONTEXT +extern __declspec(dllexport) struct ImGuiContext* GImGui; // Current implicit context pointer +#else +extern __declspec(dllimport) struct ImGuiContext* GImGui; // Current implicit context pointer +#endif + +#define GImGui GImGui diff --git a/Html/Main/Credit/credit.htm b/Html/Main/Credit/credit.htm index 569aa846f..e8104a5d5 100644 --- a/Html/Main/Credit/credit.htm +++ b/Html/Main/Credit/credit.htm @@ -359,5 +359,9 @@

Zlib

Adler

Jean-loup Gailly jloup@gzip.org

Mark Adler madler@alumni.caltech.edu

+

Font Awesome

+

Icons font

+

Fonticons, Inc. https://fontawesome.com

+

CC BY 4.0 License https://creativecommons.org/licenses/by/4.0/

- \ No newline at end of file + diff --git a/OVP/D3D9Client/CMakeLists.txt b/OVP/D3D9Client/CMakeLists.txt index ddbaf2e1c..7f7d02dda 100644 --- a/OVP/D3D9Client/CMakeLists.txt +++ b/OVP/D3D9Client/CMakeLists.txt @@ -2,6 +2,7 @@ project(D3D9Client VERSION 31.0) configure_file(D3D9ClientConfig.h.in D3D9ClientConfig.h) +FetchContent_MakeAvailable(imgui) # specify the C++ standard # set(CMAKE_CXX_STANDARD 11) @@ -67,6 +68,7 @@ set(SourceFiles WindowMgr.cpp ZTreeMgr.cpp Tilemgr2_imp.hpp + ${imgui_SOURCE_DIR}/backends/imgui_impl_dx9.cpp ) set(IncludeFiles @@ -171,6 +173,9 @@ add_library(D3D9Client MODULE target_include_directories(D3D9Client PUBLIC ${ORBITER_SOURCE_SDK_INCLUDE_DIR} ${DXSDK_DIR}/Include + ${imgui_SOURCE_DIR}/ + ${imgui_SOURCE_DIR}/backends/ + SDL3::SDL3 ) target_link_directories(D3D9Client PUBLIC @@ -199,6 +204,7 @@ target_link_libraries(D3D9Client odbccp32.lib version.lib msimg32.lib + SDL3::SDL3 ) set_target_properties(D3D9Client @@ -228,6 +234,7 @@ add_dependencies(D3D9Client ${OrbiterTgt} Orbitersdk D3D9Client_Interface + SDL3::SDL3 ) install(TARGETS D3D9Client diff --git a/OVP/D3D9Client/D3D9Client.cpp b/OVP/D3D9Client/D3D9Client.cpp index 8307faaac..8962a2617 100644 --- a/OVP/D3D9Client/D3D9Client.cpp +++ b/OVP/D3D9Client/D3D9Client.cpp @@ -40,7 +40,9 @@ #include "gcConst.h" #include #include - +#include "imgui.h" +#include "imgui_impl_dx9.h" +#include "imgui_impl_win32.h" #if defined(_MSC_VER) && (_MSC_VER <= 1700 ) // Microsoft Visual Studio Version 2012 and lower #define round(v) floor(v+0.5) @@ -380,7 +382,7 @@ bool D3D9Client::clbkInitialise() // ============================================================== // This is called when a simulation session will begin // -HWND D3D9Client::clbkCreateRenderWindow() +std::shared_ptr D3D9Client::clbkCreateRenderWindow() { _TRACE; @@ -424,8 +426,8 @@ HWND D3D9Client::clbkCreateRenderWindow() hRenderWnd = GraphicsClient::clbkCreateRenderWindow(); - LogAlw("Window Handle = %s",_PTR(hRenderWnd)); - SetWindowText(hRenderWnd, "[D3D9Client]"); + LogAlw("Window Handle = %s",_PTR(hRenderWnd->Win32Handle())); + SDL_SetWindowTitle(hRenderWnd->Inner(), "[D3D9Client]"); LogOk("Starting to initialize device and 3D environment..."); @@ -433,7 +435,7 @@ HWND D3D9Client::clbkCreateRenderWindow() WriteLog("[DirectX 9 Initialized]"); - HRESULT hr = pFramework->Initialize(hRenderWnd, GetVideoData()); + HRESULT hr = pFramework->Initialize(hRenderWnd->Win32Handle(), GetVideoData()); if (hr!=S_OK) { LogErr("ERROR: Failed to initialize 3D Framework"); @@ -441,13 +443,13 @@ HWND D3D9Client::clbkCreateRenderWindow() } RECT rect; - GetClientRect(hRenderWnd, &rect); - HDC hWnd = GetDC(hRenderWnd); + GetClientRect(hRenderWnd->Win32Handle(), &rect); + HDC hWnd = GetDC(hRenderWnd->Win32Handle()); HBRUSH hBr = CreateSolidBrush(RGB(0,0,0)); FillRect(hWnd, &rect, hBr); DeleteObject(hBr); - ReleaseDC(hRenderWnd, hWnd); - ValidateRect(hRenderWnd, NULL); // avoids white flash after splash screen + ReleaseDC(hRenderWnd->Win32Handle(), hWnd); + ValidateRect(hRenderWnd->Win32Handle(), NULL); // avoids white flash after splash screen pCaps = pFramework->GetCaps(); @@ -499,7 +501,7 @@ HWND D3D9Client::clbkCreateRenderWindow() SplashScreen(); // Warning SurfNative is not yet fully initialized here - ShowWindow(hRenderWnd, SW_SHOW); + SDL_ShowWindow(hRenderWnd->Inner()); OutputLoadStatus("Building Shader Programs...",0); @@ -593,7 +595,7 @@ void D3D9Client::clbkPostCreation() // Create Window Manager ----------------------------------------- // if (Config->gcGUIMode != 0) { - pWM = new WindowManager(hRenderWnd, ModuleInstance(), !(GetVideoData()->fullscreen)); + pWM = new WindowManager(hRenderWnd->Win32Handle(), ModuleInstance(), !(GetVideoData()->fullscreen)); if (pWM->IsOK() == false) SAFE_DELETE(pWM); } @@ -1686,11 +1688,11 @@ int D3D9Client::clbkVisEvent(OBJHANDLE hObj, VISHANDLE vis, DWORD msg, DWORD_PTR // ============================================================== // -void D3D9Client::PickTerrain(DWORD uMsg, int xpos, int ypos) +void D3D9Client::PickTerrain(Uint32 uMsg, int xpos, int ypos) { - bool bUD = (uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONUP || uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN); + bool bUD = (uMsg == SDL_EVENT_MOUSE_BUTTON_UP || uMsg == SDL_EVENT_MOUSE_BUTTON_DOWN); bool bPrs = IsGenericProcEnabled(GENERICPROC_PICK_TERRAIN) && bUD; - bool bHov = IsGenericProcEnabled(GENERICPROC_HOVER_TERRAIN) && (uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL); + bool bHov = IsGenericProcEnabled(GENERICPROC_HOVER_TERRAIN) && (uMsg == SDL_EVENT_MOUSE_MOTION || uMsg == SDL_EVENT_MOUSE_WHEEL); if (bPrs || bHov) { gcCore::PickGround pg = gcCore2::ScanScreen(xpos, ypos); @@ -1700,225 +1702,167 @@ void D3D9Client::PickTerrain(DWORD uMsg, int xpos, int ypos) } } +static bool EventIsKeyboard(Uint32 type) { + return type >= SDL_EVENT_KEY_UP && type < SDL_EVENT_MOUSE_MOTION; +} + +static bool EventIsMouse(Uint32 type) { + return type >= SDL_EVENT_MOUSE_MOTION && + type < SDL_EVENT_JOYSTICK_AXIS_MOTION; +} // ============================================================== // Message handler for render window -LRESULT D3D9Client::RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - static bool bTrackMouse = false; - static short xpos=0, ypos=0; - - D3D9Pick pick; - - if (hRenderWnd!=hWnd && uMsg!= WM_NCDESTROY) { - LogErr("Invalid Window !! RenderWndProc() called after calling clbkDestroyRenderWindow() uMsg=0x%X", uMsg); - return 0; - } - - if (bRunning && DebugControls::IsActive()) { - // Must update camera to correspond MAIN_SCENE due to Pick() function, - // because env-maps have altered camera settings - // GetScene()->UpdateCameraFromOrbiter(RENDERPASS_PICKSCENE); - // Obsolete: since moving env/cam stuff in pre-scene - } - - if (pWM) if (pWM->MainWindowProc(hWnd, uMsg, wParam, lParam)) return 0; - - - switch (uMsg) - { - case WM_MOUSELEAVE: - { - if (bTrackMouse && bRunning) GraphicsClient::RenderWndProc (hWnd, WM_LBUTTONUP, 0, 0); - return 0; - } - - case WM_MBUTTONDOWN: - { - break; - } - - case WM_RBUTTONUP: - case WM_RBUTTONDOWN: - { - int xp = GET_X_LPARAM(lParam); - int yp = GET_Y_LPARAM(lParam); - PickTerrain(uMsg, xp, yp); - break; - } - - - case WM_LBUTTONDOWN: - { - bTrackMouse = true; - xpos = GET_X_LPARAM(lParam); - ypos = GET_Y_LPARAM(lParam); - - GetScene()->vPickRay = GetScene()->GetPickingRay(xpos, ypos); - - TRACKMOUSEEVENT te; te.cbSize = sizeof(TRACKMOUSEEVENT); te.dwFlags = TME_LEAVE; te.hwndTrack = hRenderWnd; - TrackMouseEvent(&te); - - bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; - bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; - bool bPckVsl = IsGenericProcEnabled(GENERICPROC_PICK_VESSEL); - - if (DebugControls::IsActive() || bPckVsl || (bShift && bCtrl)) { - pick = GetScene()->PickScene(xpos, ypos); - if (bPckVsl) { - gcCore::PickData out; - out.hVessel = pick.vObj->GetObjectA(); - out.mesh = MESHHANDLE(pick.pMesh); - out.group = pick.group; - out.pos = _FV(pick.pos); - out.normal = _FV(pick.normal); - out.dist = pick.dist; - MakeGenericProcCall(GENERICPROC_PICK_VESSEL, sizeof(gcCore::PickData), &out); - } - } - - PickTerrain(uMsg, xpos, ypos); - - // No Debug Controls - if (bShift && bCtrl && !DebugControls::IsActive() && !oapiCameraInternal()) { - - if (!pick.pMesh) break; - - OBJHANDLE hObj = pick.vObj->Object(); - if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { - oapiSetFocusObject(hObj); - } - - break; - } - - // With Debug Controls - if (DebugControls::IsActive()) { - - DWORD flags = *(DWORD*)GetConfigParam(CFGPRM_GETDEBUGFLAGS); - - if (flags&DBG_FLAGS_PICK) { - - if (!pick.pMesh) break; - - if (bShift && bCtrl) { - OBJHANDLE hObj = pick.vObj->Object(); - if (oapiGetObjectType(hObj)==OBJTP_VESSEL) { - oapiSetFocusObject(hObj); - break; - } - } - else if (pick.group>=0) { - DebugControls::SetVisual(pick.vObj); - DebugControls::SelectMesh(pick.pMesh); - DebugControls::SelectGroup(pick.group); - DebugControls::SetGroupHighlight(true); - DebugControls::SetPickPos(pick.pos); - } - } - } - - break; - } - - case WM_LBUTTONUP: - { - int xp = GET_X_LPARAM(lParam); - int yp = GET_Y_LPARAM(lParam); - - PickTerrain(uMsg, xp, yp); - - if (DebugControls::IsActive()) { - DWORD flags = *(DWORD*)GetConfigParam(CFGPRM_GETDEBUGFLAGS); - if (flags&DBG_FLAGS_PICK) { - DebugControls::SetGroupHighlight(false); - } - } - bTrackMouse = false; - break; - } - - case WM_KEYDOWN: - { - bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000)!=0; - bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000)!=0; - if (wParam == 'C' && bShift && bCtrl) bControlPanel = !bControlPanel; - if (wParam == 'N' && bShift && bCtrl) Config->bCloudNormals = !Config->bCloudNormals; - if (wParam == 'F' && bShift && bCtrl) { - if (bFreeze) bFreezeEnable = bFreeze = false; - else bFreezeEnable = true; - } - if (wParam == 'A' && bFreeze) bFreezeRenderAll = !bFreezeRenderAll; - - break; - } - - case WM_MOUSEWHEEL: - { - if (DebugControls::IsActive()) { - short d = GET_WHEEL_DELTA_WPARAM(wParam); - if (d<-1) d=-1; - if (d>1) d=1; - double speed = *(double *)GetConfigParam(CFGPRM_GETCAMERASPEED); - speed *= (DebugControls::GetVisualSize()/100.0); - if (scene->CameraPan(_V(0,0,double(d))*2.0, speed)) return 0; - } - - PickTerrain(uMsg, xpos, ypos); - break; - } - - case WM_MOUSEMOVE: - - if (DebugControls::IsActive()) - { - - double x = double(GET_X_LPARAM(lParam) - xpos); - double y = double(GET_Y_LPARAM(lParam) - ypos); - xpos = GET_X_LPARAM(lParam); - ypos = GET_Y_LPARAM(lParam); - - if (bTrackMouse) { - double speed = *(double *)GetConfigParam(CFGPRM_GETCAMERASPEED); - speed *= (DebugControls::GetVisualSize() / 100.0); - if (scene->CameraPan(_V(-x, y, 0)*0.05, speed)) return 0; - } - } - - xpos = GET_X_LPARAM(lParam); - ypos = GET_Y_LPARAM(lParam); - - PickTerrain(uMsg, xpos, ypos); - - break; - - case WM_MOVE: - // If in windowed mode, move the Framework's window - break; - - case WM_SYSCOMMAND: - switch (wParam) { - case SC_KEYMENU: - // trap Alt system keys - return 1; - case SC_MOVE: - case SC_SIZE: - case SC_MAXIMIZE: - case SC_MONITORPOWER: - // Prevent moving/sizing and power loss in fullscreen mode - if (bFullscreen) return 1; - break; - } - break; - - case WM_SYSKEYUP: - if (bFullscreen) return 0; // trap Alt-key - break; - } - - if (!bRunning && uMsg>=0x0200 && uMsg<=0x020E) return 0; - return GraphicsClient::RenderWndProc (hWnd, uMsg, wParam, lParam); +bool D3D9Client::RenderWndProc(const SDL_Event &event, bool &wantsOut) { + static bool bTrackMouse = false; + static short xpos = 0, ypos = 0; + + D3D9Pick pick; + + if (hRenderWnd == nullptr) { + LogErr("Invalid Window !! RenderWndProc() called after calling " + "clbkDestroyRenderWindow()"); + return false; + } + + if (bRunning && DebugControls::IsActive()) { + // Must update camera to correspond MAIN_SCENE due to Pick() function, + // because env-maps have altered camera settings + // GetScene()->UpdateCameraFromOrbiter(RENDERPASS_PICKSCENE); + // Obsolete: since moving env/cam stuff in pre-scene + } + + if (pWM) + if (pWM->MainWindowProc(event)) + return true; + + if (event.type == SDL_EVENT_QUIT || + (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && + event.window.windowID == SDL_GetWindowID(hRenderWnd->Inner()))) { + wantsOut = true; + return true; + } + + if ((event.type == SDL_EVENT_WINDOW_MOUSE_LEAVE && + event.window.windowID == SDL_GetWindowID(hRenderWnd->Inner()) && bTrackMouse && bRunning) || + (event.type == SDL_EVENT_MOUSE_BUTTON_UP && + event.button.button == SDL_BUTTON_LEFT)) { + float xpf, xpy = 0; + SDL_GetMouseState(&xpf, &xpy); + + PickTerrain(event.type, xpf, xpy); + + if (DebugControls::IsActive()) { + DWORD flags = *(DWORD *)GetConfigParam(CFGPRM_GETDEBUGFLAGS); + if (flags & DBG_FLAGS_PICK) { + DebugControls::SetGroupHighlight(false); + } + } + bTrackMouse = false; + } else if ((event.type == SDL_EVENT_MOUSE_BUTTON_UP || event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) && event.button.button == SDL_BUTTON_RIGHT) { + int xp = event.button.x; + int yp = event.button.y; + PickTerrain(event.type, xp, yp); + } else if ((event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) && event.button.button == SDL_BUTTON_LEFT) { + bTrackMouse = true; + xpos = event.button.x; + ypos = event.button.y; + + GetScene()->vPickRay = GetScene()->GetPickingRay(xpos, ypos); + + bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; + bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; + bool bPckVsl = IsGenericProcEnabled(GENERICPROC_PICK_VESSEL); + + if (DebugControls::IsActive() || bPckVsl || (bShift && bCtrl)) { + pick = GetScene()->PickScene(xpos, ypos); + if (bPckVsl) { + gcCore::PickData out; + out.hVessel = pick.vObj->GetObjectA(); + out.mesh = MESHHANDLE(pick.pMesh); + out.group = pick.group; + out.pos = _FV(pick.pos); + out.normal = _FV(pick.normal); + out.dist = pick.dist; + MakeGenericProcCall(GENERICPROC_PICK_VESSEL, + sizeof(gcCore::PickData), &out); + } + } + + PickTerrain(event.type, xpos, ypos); + + // No Debug Controls + if (bShift && bCtrl && !DebugControls::IsActive() && + !oapiCameraInternal()) { + + if (!pick.pMesh) + return true; + + OBJHANDLE hObj = pick.vObj->Object(); + if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { + oapiSetFocusObject(hObj); + } + + return true; + } + + // With Debug Controls + if (DebugControls::IsActive()) { + + DWORD flags = *(DWORD *)GetConfigParam(CFGPRM_GETDEBUGFLAGS); + + if (flags & DBG_FLAGS_PICK) { + if (!pick.pMesh) + return true; + + if (bShift && bCtrl) { + OBJHANDLE hObj = pick.vObj->Object(); + if (oapiGetObjectType(hObj) == OBJTP_VESSEL) { + oapiSetFocusObject(hObj); + return true; + } + } else if (pick.group >= 0) { + DebugControls::SetVisual(pick.vObj); + DebugControls::SelectMesh(pick.pMesh); + DebugControls::SelectGroup(pick.group); + DebugControls::SetGroupHighlight(true); + DebugControls::SetPickPos(pick.pos); + } + } + } + } else if (event.type == SDL_EVENT_KEY_DOWN) { + bool bShift = (event.key.mod & SDL_KMOD_SHIFT) != 0; + bool bCtrl = (event.key.mod & SDL_KMOD_CTRL) != 0; + if (event.key.key == SDLK_C && bShift && bCtrl) + bControlPanel = !bControlPanel; + if (event.key.key == SDLK_N && bShift && bCtrl) + Config->bCloudNormals = !Config->bCloudNormals; + if (event.key.key == SDLK_F && bShift && bCtrl) { + if (bFreeze) + bFreezeEnable = bFreeze = false; + else + bFreezeEnable = true; + } + if (event.key.key == SDLK_A && bFreeze) + bFreezeRenderAll = !bFreezeRenderAll; + } else if (event.type == SDL_EVENT_MOUSE_WHEEL) { + if (DebugControls::IsActive()) { + short d = event.wheel.y; + if (d < -1) + d = -1; + if (d > 1) + d = 1; + double speed = *(double *)GetConfigParam(CFGPRM_GETCAMERASPEED); + speed *= (DebugControls::GetVisualSize() / 100.0); + if (scene->CameraPan(_V(0, 0, double(d)) * 2.0, speed)) + return 0; + } + + PickTerrain(event.type, xpos, ypos); + } + + return GraphicsClient::RenderWndProc(event, wantsOut); } @@ -2104,9 +2048,9 @@ bool oapi::D3D9Client::SaveSurfaceToFile (const D3DSURFACE_DESC* desc, D3DLOCKED bool oapi::D3D9Client::SaveSurfaceToClipboard (const D3DSURFACE_DESC* desc) { - if (OpenClipboard(hRenderWnd)) + if (OpenClipboard(hRenderWnd->Win32Handle())) { - HDC hDC = GetDC(hRenderWnd); + HDC hDC = GetDC(hRenderWnd->Win32Handle()); HDC hdcmem = CreateCompatibleDC(hDC); HBITMAP hBm = CreateCompatibleBitmap(hDC, desc->Width, desc->Height); @@ -2724,7 +2668,57 @@ bool D3D9Client::clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int _TRACE; return FilterElevationPhysics(hPlanet, lvl, ilat, ilng, elev_res, elev); } +void D3D9Client::clbkImGuiNewFrame() +{ + _TRACE; + ImGui_ImplDX9_NewFrame(); +} +void D3D9Client::clbkImGuiRenderDrawData() +{ + _TRACE; + + if (pDevice->BeginScene() >= 0) + { + ImGui::Render(); + ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); + pDevice->EndScene(); + } + + // Update and Render additional Platform Windows + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + for(auto &surf: ImTextures) { + clbkReleaseSurface(surf); + } + ImTextures.clear(); +} +void D3D9Client::clbkImGuiInit() +{ + _TRACE; + ImGui_ImplDX9_Init(pDevice); +} +void D3D9Client::clbkImGuiShutdown() +{ + _TRACE; + // Clean up also here just in case + for(auto &surf: ImTextures) { + clbkReleaseSurface(surf); + } + ImTextures.clear(); + ImGui_ImplDX9_Shutdown(); +} +uint64_t D3D9Client::clbkImGuiSurfaceTexture(SURFHANDLE surf) +{ + ImTextures.push_back(surf); + clbkIncrSurfaceRef(surf); + LPDIRECT3DTEXTURE9 pTxt = SURFACE(surf)->GetTexture(); + return (uint64_t)pTxt; +} // ======================================================================= bool D3D9Client::clbkSplashLoadMsg (const char *msg, int line) @@ -2924,7 +2918,7 @@ void D3D9Client::SplashScreen() RECT rS; - GetWindowRect(hRenderWnd, &rS); + GetWindowRect(hRenderWnd->Win32Handle(), &rS); LogAlw("Splash Window Size = [%u, %u]", rS.right - rS.left, rS.bottom - rS.top); LogAlw("Splash Window LeftTop = [%d, %d]", rS.left, rS.top); diff --git a/OVP/D3D9Client/D3D9Client.h b/OVP/D3D9Client/D3D9Client.h index c1015c2eb..1de1296ea 100644 --- a/OVP/D3D9Client/D3D9Client.h +++ b/OVP/D3D9Client/D3D9Client.h @@ -525,23 +525,7 @@ class D3D9Client : public GraphicsClient */ ScreenAnnotation* clbkCreateAnnotation(); - /** - * \brief Render window message handler - * - * Derived classes should also call the base class method to allow - * default message processing. - * \param hWnd render window handle - * \param uMsg Windows message identifier - * \param wParam WPARAM message parameter - * \param lParam LPARAM message parameter - * \return The return value depends on the message being processed. - * \note This is the standard Windows message handler for the render - * window. - * \note This method currently intercepts only the WM_CLOSE and WM_DESTROY - * messages, and passes everything else to the Orbiter core message - * handler. - */ - LRESULT RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + bool RenderWndProc(const SDL_Event &event, bool &wantsOut) override; /** * \brief Message handler for 'video' tab in Orbiter Launchpad dialog @@ -1037,8 +1021,12 @@ class D3D9Client : public GraphicsClient bool clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev); // @} + void clbkImGuiNewFrame() override; + void clbkImGuiRenderDrawData() override; + void clbkImGuiInit() override; + void clbkImGuiShutdown() override; + uint64_t clbkImGuiSurfaceTexture(SURFHANDLE surf) override; - HWND GetRenderWindow () const { return hRenderWnd; } CD3DFramework9 * GetFramework() const { return pFramework; } Scene * GetScene() const { return scene; } MeshManager * GetMeshMgr() const { return meshmgr; } @@ -1066,7 +1054,7 @@ class D3D9Client : public GraphicsClient bool IsGenericProcEnabled(DWORD id) const; void SetScenarioName(const std::string &path) { scenarioName = path; }; void HackFriendlyHack(); - void PickTerrain(DWORD uMsg, int xpos, int ypos); + void PickTerrain(Uint32 uMsg, int xpos, int ypos); DEVMESHHANDLE GetDevMesh(MESHHANDLE hMesh); HANDLE GetMainThread() const { return hMainThread; } @@ -1114,7 +1102,7 @@ class D3D9Client : public GraphicsClient * \note Derived classes should perform any required per-session * initialisation of the 3D render environment here. */ - HWND clbkCreateRenderWindow (); + std::shared_ptr clbkCreateRenderWindow () override; /** * \brief Simulation startup finalisation @@ -1311,7 +1299,7 @@ class D3D9Client : public GraphicsClient const char * pCustomSplashScreen; DWORD pSplashTextColor; - HWND hRenderWnd; // render window handle + std::shared_ptr hRenderWnd; // render window handle bool bControlPanel; bool bScatterUpdate; @@ -1341,6 +1329,7 @@ class D3D9Client : public GraphicsClient std::vector RenderProcs; std::vector GenericProcs; + std::vector ImTextures; mutable std::list RenderStack; HFONT hLblFont1; diff --git a/OVP/D3D9Client/D3D9Config.h b/OVP/D3D9Client/D3D9Config.h index c23be7f34..8f10ce7f2 100644 --- a/OVP/D3D9Client/D3D9Config.h +++ b/OVP/D3D9Client/D3D9Config.h @@ -54,7 +54,7 @@ class D3D9Config { double Separation; ///< StereoScopic 3D depth of field separation \[m\] (10.0...100.0, default=65) double SunAngle; ///< Sun-angle above horizon when night-lights set it \[deg\] (0.1...20.0, default=10) double BumpAmp; ///< Bump map amplification setting (0.1...10.0, default=1) - double PlanetGlow; ///< Intensity of planet glow effect (0.01...2.0, default=0.7) + float PlanetGlow; ///< Intensity of planet glow effect (0.01...2.0, default=0.7) double FrameRate; ///< Frame-rate limiter double OrbitalShadowMult; ///< Multiplier for cloud shadows for Orbital flight int EnableLimiter; ///< Enable frame-rate limiter @@ -100,13 +100,13 @@ class D3D9Config { int NoPlanetAA; ///< Disable planet surface anti-aliasing to prevent white pixels at horizon char *DebugFont; ///< Font face for debug lines (default="Fixed") char *SolCfg; ///< Solar system to use (default="Sol") - double GFXIntensity; ///< Post Processing | Light glow intensity (0.0...1.0, default=0.5) - double GFXDistance; ///< Post Processing | Light glow distance (0.0...1.0, default=0.8) - double GFXThreshold; ///< Post Processing | Glow threshold (0.5...2.0, default=1.1) - double GFXGamma; ///< Post Processing | Gamma (0.3...2.5, default=1.0) - double GFXSunIntensity; ///< Light Configuration| Sunlight Intensity (0.5...2.5, default=1.2) - double GFXLocalMax; ///< Light Configuration| Local Lights Max (0.001...1.0, default=0.5) - double GFXGlare; ///< Sun glare intensity| (0.001...1.0, default=0.5) + float GFXIntensity; ///< Post Processing | Light glow intensity (0.0...1.0, default=0.5) + float GFXDistance; ///< Post Processing | Light glow distance (0.0...1.0, default=0.8) + float GFXThreshold; ///< Post Processing | Glow threshold (0.5...2.0, default=1.1) + float GFXGamma; ///< Post Processing | Gamma (0.3...2.5, default=1.0) + float GFXSunIntensity; ///< Light Configuration| Sunlight Intensity (0.5...2.5, default=1.2) + float GFXLocalMax; ///< Light Configuration| Local Lights Max (0.001...1.0, default=0.5) + float GFXGlare; ///< Sun glare intensity| (0.001...1.0, default=0.5) std::map AtmoCfg; diff --git a/OVP/D3D9Client/D3D9Pad.cpp b/OVP/D3D9Client/D3D9Pad.cpp index e16640939..73360e1c5 100644 --- a/OVP/D3D9Client/D3D9Pad.cpp +++ b/OVP/D3D9Client/D3D9Pad.cpp @@ -1304,7 +1304,8 @@ void D3D9Pad::Polygon (const IVECTOR2 *pt, int npt) #endif if (npt<3) return; - if (HasBrush() && npt > 64) return; + // The VectorMap drawing code creates polygons with at least 68 points + if (HasBrush() && npt > 70) return; // Create filled polygon interior ----------------------------------------- diff --git a/OVP/D3D9Client/DebugControls.cpp b/OVP/D3D9Client/DebugControls.cpp index dad0ce16e..828323d06 100644 --- a/OVP/D3D9Client/DebugControls.cpp +++ b/OVP/D3D9Client/DebugControls.cpp @@ -18,6 +18,8 @@ #include "MaterialMgr.h" #include "VectorHelpers.h" #include +#include +#include enum scale { LIN, SQRT, SQR }; @@ -40,7 +42,7 @@ float cpr, cpg, cpb, cpa; double resbias = 4.0; char visual[64]; int origwidth; -HWND hGfxDlg = NULL; +GFXDialog *gfxDlg; HWND hDlg = NULL; HWND hDataWnd = NULL; vObject *vObj = NULL; @@ -57,10 +59,37 @@ char OpenFileName[255]; char SaveFileName[255]; void UpdateMaterialDisplay(bool bSetup=false); -void SetGFXSliders(); -INT_PTR CALLBACK WndProcGFX(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + void OpenGFXDlgClbk(void *context); +class GFXDialog: public ImGuiDialog +{ +public: + GFXDialog():ImGuiDialog("Graphics Controls") {} + void OnDraw() override { + ImGui::PushItemWidth(150.0); + ImGui::SeparatorText("Post Processing Configuration"); + + + ImGui::SliderFloatReset("Light glow intensity", &Config->GFXIntensity, 0.0f, 1.0f, 0.5f, "%1.2f"); + ImGui::SliderFloatReset("Light glow distance", &Config->GFXDistance, 0.0f, 1.0f, 0.8f, "%1.2f"); + ImGui::SliderFloatReset("Glow threshold", &Config->GFXThreshold, 0.5f, 2.0f, 1.1f, "%1.2f"); + ImGui::SliderFloatReset("Gamma", &Config->GFXGamma, 0.3f, 2.5f, 1.0f, "%1.2f"); + + ImGui::SeparatorText("Light Configuration"); + + ImGui::SliderFloatReset("Sunlight Intensity", &Config->GFXSunIntensity, 0.5f, 2.5f, 1.2f, "%1.2f"); + ImGui::SliderFloatReset("Indirect Lighting", &Config->PlanetGlow, 0.01f, 2.0f, 0.7f, "%1.2f"); + ImGui::SliderFloatReset("Local Lights Max", &Config->GFXLocalMax, 0.001f, 1.0f, 0.5f, "%1.2f"); + ImGui::SliderFloatReset("Sun Glare Intensity", &Config->GFXGlare, 0.001f, 1.0f, 0.5f, "%1.2f"); + + if(ImGui::Button("Recrete Sun/Glares")) { + g_client->GetScene()->CreateSunGlare(); + } + ImGui::PopItemWidth(); + } +}; + struct _Variable { float min, max, extmax, def; @@ -158,7 +187,6 @@ void Create() { vObj = NULL; hDlg = NULL; - hGfxDlg = NULL; nMesh = 0; nGroup = 0; sMesh = 0; @@ -179,7 +207,8 @@ void Create() dwCmd = 0; } - dwGFX = oapiRegisterCustomCmd((char*)"D3D9 Graphics Controls", (char*)"This dialog allows to control various graphics options", OpenGFXDlgClbk, NULL); + gfxDlg = new GFXDialog(); + dwGFX = oapiRegisterCustomCmd((char*)"D3D9 Graphics Controls", (char*)"This dialog allows to control various graphics options", OpenGFXDlgClbk, gfxDlg); resbias = 4.0 + Config->LODBias; @@ -269,7 +298,9 @@ void Release() { vObj = NULL; hDlg = NULL; - hGfxDlg = NULL; + oapiCloseDialog(gfxDlg); + delete gfxDlg; + gfxDlg = NULL; if (dwCmd) oapiUnregisterCustomCmd(dwCmd); if (dwGFX) oapiUnregisterCustomCmd(dwGFX); dwCmd = NULL; @@ -2059,285 +2090,14 @@ INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return oapiDefDialogProc(hWnd, uMsg, wParam, lParam); } - - - - - -// ============================================================================================= -// -void CloseGFX() -{ - if (hGfxDlg != NULL) { - oapiCloseDialog(hGfxDlg); - hGfxDlg = NULL; - } -} - // ============================================================================================= // void OpenGFXDlgClbk(void *context) { - HWND l_hDlg = oapiOpenDialog(g_hInst, IDD_GRAPHICS, WndProcGFX); - if (l_hDlg) hGfxDlg = l_hDlg; // otherwise open already - else return; - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETPOS, 1, 0); - - // slider - SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETRANGEMAX, 1, 255); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETRANGEMIN, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETPOS, 1, 0); - - - - // reset-button(s) - HANDLE hImg = LoadImage(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_LOADTRANSPARENT | LR_DEFAULTSIZE); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_INTENSITY_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_DISTANCE_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_THRESHOLD_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_GAMMA_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_SUNLIGHT_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_IRRADIANCE_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_LOCALMAX_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - SendMessage(GetDlgItem(hGfxDlg, IDC_GFX_GLARE_RESET), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImg); - - CreateToolTip(IDC_GFX_INTENSITY_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_DISTANCE_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_THRESHOLD_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_GAMMA_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_SUNLIGHT_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_IRRADIANCE_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_LOCALMAX_RESET, hGfxDlg, (char*)"Reset to default"); - CreateToolTip(IDC_GFX_GLARE_RESET, hGfxDlg, (char*)"Reset to default"); - - SetGFXSliders(); -} - -void SetGFXSliders() -{ - char lbl[32]; - double fpos; - - fpos = Config->GFXIntensity; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL1), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_SETPOS, 1, WORD(fpos*255.0)); - - fpos = Config->GFXDistance; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL2), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_SETPOS, 1, WORD(fpos*255.0)); - - fpos = Config->GFXThreshold; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL3), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.0)); - - fpos = Config->GFXGamma; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL4), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.5)); - - fpos = Config->GFXSunIntensity; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL5), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.0)); - - fpos = Config->PlanetGlow; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL6), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_SETPOS, 1, WORD(fpos*255.0 / 2.0)); - - fpos = Config->GFXLocalMax; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL7), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_SETPOS, 1, WORD(fpos*255.0)); - - fpos = Config->GFXGlare; - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL8), lbl); - SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_SETPOS, 1, WORD(fpos * 255.0)); + GFXDialog *gfx = (GFXDialog *)context; + oapiOpenDialog(gfx); } -void ReadGFXSliders() -{ - char lbl[32]; - double fpos; - - fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_INTENSITY, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL1), lbl); - Config->GFXIntensity = fpos; - - fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_DISTANCE, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL2), lbl); - Config->GFXDistance = fpos; - - fpos = (2.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_THRESHOLD, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL3), lbl); - Config->GFXThreshold = fpos; - - fpos = (2.5 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_GAMMA, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL4), lbl); - Config->GFXGamma = fpos; - - fpos = (2.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_SUNLIGHT, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL5), lbl); - Config->GFXSunIntensity = fpos; - - fpos = (2.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_IRRADIANCE, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL6), lbl); - Config->PlanetGlow = fpos; - - fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_LOCALMAX, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL7), lbl); - Config->GFXLocalMax = fpos; - - fpos = (1.0 / 255.0) * double(SendDlgItemMessage(hGfxDlg, IDC_GFX_GLARE, TBM_GETPOS, 0, 0)); - sprintf_s(lbl, 32, "%1.2f", fpos); - SetWindowTextA(GetDlgItem(hGfxDlg, IDC_GFX_VAL8), lbl); - Config->GFXGlare = fpos; -} - -INT_PTR CALLBACK WndProcGFX(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - - case WM_INITDIALOG: - { - return TRUE; // All Init actions are done in OpenDlgClbk(); - } - - case WM_HSCROLL: - { - if (LOWORD(wParam) == TB_THUMBTRACK || LOWORD(wParam) == TB_ENDTRACK) { - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_INTENSITY)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_DISTANCE)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_THRESHOLD)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_GAMMA)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_SUNLIGHT)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_IRRADIANCE)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_LOCALMAX)) ReadGFXSliders(); - if (HWND(lParam) == GetDlgItem(hWnd, IDC_GFX_GLARE)) ReadGFXSliders(); - } - return false; - } - - case WM_COMMAND: - - switch (LOWORD(wParam)) { - - case IDCANCEL: - CloseGFX(); - break; - - case IDC_GFX_RECOMPILE: - g_client->GetScene()->CreateSunGlare(); - break; - - - case IDC_GFX_INTENSITY_RESET: - Config->GFXIntensity = 0.5; - SetGFXSliders(); - break; - - case IDC_GFX_DISTANCE_RESET: - Config->GFXDistance = 0.8; - SetGFXSliders(); - break; - - case IDC_GFX_THRESHOLD_RESET: - Config->GFXThreshold = 1.1; - SetGFXSliders(); - break; - - case IDC_GFX_GAMMA_RESET: - Config->GFXGamma = 1.0; - SetGFXSliders(); - break; - - case IDC_GFX_SUNLIGHT_RESET: - Config->GFXSunIntensity = 1.2; - SetGFXSliders(); - break; - - case IDC_GFX_IRRADIANCE_RESET: - Config->PlanetGlow = 1.0; - SetGFXSliders(); - break; - - case IDC_GFX_LOCALMAX_RESET: - Config->GFXLocalMax = 0.5; - SetGFXSliders(); - break; - - case IDC_GFX_GLARE_RESET: - Config->GFXGlare = 0.3; - SetGFXSliders(); - break; - - default: - LogErr("WndProcGFX() LOWORD(%hu), HIWORD(0x%hX)", LOWORD(wParam), HIWORD(wParam)); - break; - } - break; - } - - return oapiDefDialogProc(hWnd, uMsg, wParam, lParam); -} - - - - } //namespace diff --git a/OVP/D3D9Client/DebugControls.h b/OVP/D3D9Client/DebugControls.h index b05e4a676..60f245e41 100644 --- a/OVP/D3D9Client/DebugControls.h +++ b/OVP/D3D9Client/DebugControls.h @@ -69,6 +69,7 @@ class vObject; // ============================================================== namespace DebugControls { + class GFXDialog; extern DWORD sMesh; extern DWORD sGroup; @@ -79,6 +80,7 @@ namespace DebugControls { extern double camSpeed; extern double resbias; extern std::map Emitters; + extern GFXDialog *gfxDlg; /** * \brief Same functionality than 'official' GetConfigParam, but for diff --git a/OVP/D3D9Client/Log.cpp b/OVP/D3D9Client/Log.cpp index 2a8a9f86d..95d699640 100644 --- a/OVP/D3D9Client/Log.cpp +++ b/OVP/D3D9Client/Log.cpp @@ -75,7 +75,7 @@ void RuntimeError(const char* File, const char* Fnc, UINT Line) if (Config->DebugLvl == 0) return; char buf[256]; sprintf_s(buf, 256, "[%s] [%s] Line: %u See Orbiter.log for details.", File, Fnc, Line); - MessageBoxA(g_client->GetRenderWindow(), buf, "Critical Error:", MB_OK); + MessageBoxA(g_client->GetRenderWindow()->Win32Handle(), buf, "Critical Error:", MB_OK); DebugBreak(); } diff --git a/OVP/D3D9Client/WindowMgr.cpp b/OVP/D3D9Client/WindowMgr.cpp index 1d86a8fc3..7743bc351 100644 --- a/OVP/D3D9Client/WindowMgr.cpp +++ b/OVP/D3D9Client/WindowMgr.cpp @@ -1059,6 +1059,18 @@ SideBar *WindowManager::FindDestination() // =============================================================================================== // Orbiter Application Main Window Proc // +bool WindowManager::MainWindowProc(const SDL_Event &event) +{ + static int xpos, ypos; + if (event.type == SDL_EVENT_MOUSE_MOTION) { + xpos = event.motion.x; + ypos = event.motion.y; + MouseMoved(xpos, ypos); + return true; + } + return false; +} + bool WindowManager::MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int xpos, ypos; @@ -1088,13 +1100,6 @@ bool WindowManager::MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l return false; } - - - - - - - // =============================================================================================== // SideBar Implementation // =============================================================================================== @@ -1542,20 +1547,20 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar static bool bUpdate = false; HWND hMain = pMgr->GetMainWindow(); - + switch(uMsg) { - + case WM_KEYDOWN: { pMgr->MainWindowProc(hWnd, uMsg, wParam, lParam); break; } - - + + case WM_MOUSEWHEEL: { if (GetStyle() == gcGUI::DS_FLOAT) break; - + int old = rollpos; short d = GET_WHEEL_DELTA_WPARAM(wParam); if (d>0) rollpos += pMgr->cfg.scroll; @@ -1572,25 +1577,25 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar { xpos = GET_X_LPARAM(lParam); ypos = GET_Y_LPARAM(lParam); - + for (Node* nd : wList) { Node *pPar = nd->pParent; - + if (pPar) { if (pPar->GetSideBar() == this) { if (pPar->bOpen == false) continue; } } - + if (PointInside(xpos, ypos, &(nd->trect))) { - + if (nd->bClose && PointInside(xpos, ypos, &(nd->crect))) { dnClose = nd; } - else { + else { xof = xpos - nd->trect.left; - yof = ypos - nd->trect.top; + yof = ypos - nd->trect.top; dnNode = nd; } TRACKMOUSEEVENT te; te.cbSize = sizeof(TRACKMOUSEEVENT); te.dwFlags = TME_LEAVE; te.hwndTrack = hBar; @@ -1600,14 +1605,14 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar } break; } - + case WM_LBUTTONUP: { int xp = GET_X_LPARAM(lParam); int yp = GET_Y_LPARAM(lParam); - + SideBar *pDG = pMgr->GetDraged(); - + if (pDG) { SideBar *pTgt = pMgr->FindDestination(); if (pTgt) pTgt->Apply(); @@ -1629,7 +1634,7 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar } } } - + if (dnClose) { if (dnClose->GetSideBar() == this) { if (PointInside(xp, yp, &(dnClose->crect))) { @@ -1640,33 +1645,33 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar } } } - + dnNode = NULL; dnClose = NULL; break; } - - + + case WM_MOUSELEAVE: { dnNode = NULL; dnClose = NULL; - break; + break; } - - + + case WM_MOUSEMOVE: { int dx = abs(GET_X_LPARAM(lParam) - xpos); int dy = abs(GET_Y_LPARAM(lParam) - ypos); int x = GET_X_LPARAM(lParam); int y = GET_Y_LPARAM(lParam); - + if (Config->gcGUIMode < 3) { - + POINT scp = { x, y }; ClientToScreen(hWnd, &scp); - + // Begin Moving a Window // if (dnNode && IsFloater() && (GetTopNode() == dnNode) && (dx > 1 || dy > 1)) @@ -1678,7 +1683,7 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar dnClose = NULL; break; } - + // Detach a window from a dock // if (Config->gcGUIMode == 1) { @@ -1691,7 +1696,7 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar break; } } - + // Move a dragged window and try to insert content into a dock // if (pMgr->GetDraged()) { @@ -1709,21 +1714,20 @@ LRESULT SideBar::SideBarWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar break; } } - break; + break; } - + case WM_PAINT: PaintWindow(); break; - + case WM_ERASEBKGND: return 1; - + default: break; } - - + return DefWindowProc(hWnd, uMsg, wParam, lParam); } diff --git a/OVP/D3D9Client/WindowMgr.h b/OVP/D3D9Client/WindowMgr.h index 9f9f02f8c..bc4e0a481 100644 --- a/OVP/D3D9Client/WindowMgr.h +++ b/OVP/D3D9Client/WindowMgr.h @@ -23,11 +23,13 @@ #ifndef __WNDMGR_H #define __WNDMGR_H -#include #include "gcCore.h" -#include -#include #include "gcGUI.h" +#include +#include +#include + +#include using namespace std; @@ -176,7 +178,8 @@ class WindowManager : public gcGUIBase // =============================================================================================== // - bool MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + bool MainWindowProc(const SDL_Event &event); + bool MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void Animate(); int GetWidth() const { return width; } HWND GetMainWindow() const { return hMainWnd; } diff --git a/OVP/D3D9Client/gcConst.cpp b/OVP/D3D9Client/gcConst.cpp index e319f9e3c..4498986e0 100644 --- a/OVP/D3D9Client/gcConst.cpp +++ b/OVP/D3D9Client/gcConst.cpp @@ -328,7 +328,7 @@ HBITMAP gcConst::LoadBitmapFromFile(const char* fname) // HWND gcConst::GetRenderWindow() { - return g_client->GetRenderWindow(); + return g_client->GetRenderWindow()->Win32Handle(); } diff --git a/OVP/D3D9Client/gcCore.cpp b/OVP/D3D9Client/gcCore.cpp index 76c0d04be..8d52324d0 100644 --- a/OVP/D3D9Client/gcCore.cpp +++ b/OVP/D3D9Client/gcCore.cpp @@ -567,7 +567,7 @@ HBITMAP gcCore::LoadBitmapFromFile(const char* fname) // HWND gcCore::GetRenderWindow() { - return g_client->GetRenderWindow(); + return g_client->GetRenderWindow()->Win32Handle(); } diff --git a/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.cpp b/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.cpp index c3a3f61fd..d872b6b97 100644 --- a/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.cpp +++ b/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.cpp @@ -1135,7 +1135,7 @@ void ToolKit::clbkMouseClick(int iUser, void *pData) // ================================================================================================= // Orbiter Module Callback // -bool ToolKit::clbkProcessMouse(UINT event, DWORD state, DWORD x, DWORD y) +bool ToolKit::clbkProcessMouse(const SDL_Event& event, DWORD x, DWORD y) { if (!pCore) return false; @@ -1148,7 +1148,7 @@ bool ToolKit::clbkProcessMouse(UINT event, DWORD state, DWORD x, DWORD y) pick.dist = 1e16f; pick.grp_inst = -1; - if (event == WM_LBUTTONDOWN) { + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { down_corner = -1; // down_corner is the ID of the draggable corner balls if (bImport && hOverlay) { for (int i = 0; i < 4; i++) { @@ -1160,7 +1160,7 @@ bool ToolKit::clbkProcessMouse(UINT event, DWORD state, DWORD x, DWORD y) } } - if (event == WM_LBUTTONUP) down_corner = -1; + if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) down_corner = -1; if ((x != xpos || y != ypos) && down_corner >= 0) diff --git a/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.h b/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.h index e9d519a18..008d72884 100644 --- a/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.h +++ b/OVP/D3D9Client/samples/TerrainToolKit/ToolKit.h @@ -129,7 +129,7 @@ class ToolKit : public gcGUIApp, public oapi::Module void clbkSimulationStart(RenderMode rm); void clbkSimulationEnd(); void clbkPreStep(double simt, double simdt, double mjd); - bool clbkProcessMouse(UINT event, DWORD state, DWORD x, DWORD y); + bool clbkProcessMouse(const SDL_Event& event, DWORD x, DWORD y) override; bool clbkProcessKeyboardBuffered(DWORD key, char kstate[256], bool simRunning); // From D3D9Client diff --git a/OVP/GDIClient/CMakeLists.txt b/OVP/GDIClient/CMakeLists.txt index 1e424bccd..60780f912 100644 --- a/OVP/GDIClient/CMakeLists.txt +++ b/OVP/GDIClient/CMakeLists.txt @@ -10,17 +10,19 @@ add_library(GDIClient SHARED ) target_include_directories(GDIClient - PUBLIC ${CMAKE_SOURCE_DIR}/Orbitersdk/include + PUBLIC ${CMAKE_SOURCE_DIR}/Orbitersdk/include imgui ) add_dependencies(GDIClient ${OrbiterTgt} Orbitersdk + imgui ) target_link_libraries(GDIClient ${ORBITER_LIB} ${ORBITER_SDK_LIB} + imgui ) # Installation diff --git a/OVP/GDIClient/GDIClient.cpp b/OVP/GDIClient/GDIClient.cpp index 37f6cf047..b5364638e 100644 --- a/OVP/GDIClient/GDIClient.cpp +++ b/OVP/GDIClient/GDIClient.cpp @@ -104,7 +104,7 @@ bool GDIClient::clbkSaveSurfaceToImage (SURFHANDLE surf, const char *fname, Imag if (fname == NULL) { // copy device-dependent bitmap to clipboard - if (OpenClipboard (GetRenderWindow())) { + if (OpenClipboard (GetRenderWindow()->Win32Handle())) { EmptyClipboard(); SetClipboardData(CF_BITMAP,hbm); CloseClipboard(); diff --git a/Orbitersdk/CMakeLists.txt b/Orbitersdk/CMakeLists.txt index 8c24a8f87..912ad8f1a 100644 --- a/Orbitersdk/CMakeLists.txt +++ b/Orbitersdk/CMakeLists.txt @@ -31,6 +31,7 @@ add_custom_target(copy_sdk_include ) add_dependencies(Orbitersdk copy_sdk_include + SDL3::SDL3 ) if(ORBITER_MAKE_DOC) diff --git a/Orbitersdk/include/GraphicsAPI.h b/Orbitersdk/include/GraphicsAPI.h index ba7d4fbac..d64c38e98 100644 --- a/Orbitersdk/include/GraphicsAPI.h +++ b/Orbitersdk/include/GraphicsAPI.h @@ -12,6 +12,8 @@ #define __GRAPHICSAPI_H #include "Orbitersdk.h" + +#include #include #include @@ -788,25 +790,17 @@ class OAPIFUNC GraphicsClient: public Module { /** * \brief Returns the handle of the main render window. */ - HWND GetRenderWindow () const { return hRenderWnd; } + const std::shared_ptr& GetRenderWindow () const { return hRenderWnd; } - /** - * \brief Render window message handler - * - * Derived classes should also call the base class method to allow - * default message processing. - * \param hWnd render window handle - * \param uMsg Windows message identifier - * \param wParam WPARAM message parameter - * \param lParam LPARAM message parameter - * \return The return value depends on the message being processed. - * \note This is the standard Windows message handler for the render - * window. - * \note This method currently intercepts only the WM_CLOSE and WM_DESTROY - * messages, and passes everything else to the Orbiter core message - * handler. - */ - virtual LRESULT RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + /** + * \brief Event handler for the render window. + * + * Be sure to call ConsumeEvent on your ImCtx here. + * \param event The event to consume + * \param wantsOut Set to true if the render window should be closed + * \return True if the event was consumed + */ + virtual bool RenderWndProc(const SDL_Event &event, bool &wantsOut); /** * \brief Message handler for 'video' tab in Orbiter Launchpad dialog @@ -1481,6 +1475,18 @@ class OAPIFUNC GraphicsClient: public Module { virtual bool clbkFilterElevation(OBJHANDLE hPlanet, int ilat, int ilng, int lvl, double elev_res, INT16* elev) { return false; } // @} + virtual void clbkImGuiNewFrame () = 0; + virtual void clbkImGuiRenderDrawData () = 0; + virtual void clbkImGuiInit () = 0; + virtual void clbkImGuiShutdown() = 0; + // Returns an ImTextureID from a surface so that it can be used in + // ImGui widgets. + // Note: we use uint64_t so we don't have to include imgui.h + // This method should make sure the texture won't be released + // before the frame is ended by e.g. incrementing its reference + // counter and releasing it once the frame has been rendered. + virtual uint64_t clbkImGuiSurfaceTexture(SURFHANDLE surf) = 0; + protected: /** \brief Launchpad video tab indicator * @@ -1509,7 +1515,7 @@ class OAPIFUNC GraphicsClient: public Module { * \note Derived classes should perform any required per-session * initialisation of the 3D render environment here. */ - virtual HWND clbkCreateRenderWindow (); + virtual std::shared_ptr clbkCreateRenderWindow (); /** * \brief Simulation startup finalisation @@ -1831,9 +1837,9 @@ class OAPIFUNC GraphicsClient: public Module { * \return Render window handle * \note This is called after clbkCreateRenderWindow returns. */ - HWND InitRenderWnd (HWND hWnd); + void InitRenderWnd (std::shared_ptr& hWnd); - HWND hRenderWnd; // render window handle + std::shared_ptr hRenderWnd; // render window handle HINSTANCE hOrbiterInst; // orbiter core instance handle VIDEODATA VideoData; // the standard video options from config diff --git a/Orbitersdk/include/IconsFontAwesome6.h b/Orbitersdk/include/IconsFontAwesome6.h new file mode 100644 index 000000000..bc878361a --- /dev/null +++ b/Orbitersdk/include/IconsFontAwesome6.h @@ -0,0 +1,1416 @@ +// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py +// for C and C++ +// from codepoints https://github.com/FortAwesome/Font-Awesome/raw/6.x/metadata/icons.yml +// for use with font https://github.com/FortAwesome/Font-Awesome/blob/6.x/webfonts/fa-regular-400.ttf, https://github.com/FortAwesome/Font-Awesome/blob/6.x/webfonts/fa-solid-900.ttf + +#pragma once + +#define FONT_ICON_FILE_NAME_FAR "fa-regular-400.ttf" +#define FONT_ICON_FILE_NAME_FAS "fa-solid-900.ttf" + +#define ICON_MIN_FA 0xe005 +#define ICON_MAX_16_FA 0xf8ff +#define ICON_MAX_FA 0xf8ff + +#define ICON_FA_0 "0" // U+0030 +#define ICON_FA_1 "1" // U+0031 +#define ICON_FA_2 "2" // U+0032 +#define ICON_FA_3 "3" // U+0033 +#define ICON_FA_4 "4" // U+0034 +#define ICON_FA_5 "5" // U+0035 +#define ICON_FA_6 "6" // U+0036 +#define ICON_FA_7 "7" // U+0037 +#define ICON_FA_8 "8" // U+0038 +#define ICON_FA_9 "9" // U+0039 +#define ICON_FA_A "A" // U+0041 +#define ICON_FA_ADDRESS_BOOK "\xef\x8a\xb9" // U+f2b9 +#define ICON_FA_ADDRESS_CARD "\xef\x8a\xbb" // U+f2bb +#define ICON_FA_ALIGN_CENTER "\xef\x80\xb7" // U+f037 +#define ICON_FA_ALIGN_JUSTIFY "\xef\x80\xb9" // U+f039 +#define ICON_FA_ALIGN_LEFT "\xef\x80\xb6" // U+f036 +#define ICON_FA_ALIGN_RIGHT "\xef\x80\xb8" // U+f038 +#define ICON_FA_ANCHOR "\xef\x84\xbd" // U+f13d +#define ICON_FA_ANCHOR_CIRCLE_CHECK "\xee\x92\xaa" // U+e4aa +#define ICON_FA_ANCHOR_CIRCLE_EXCLAMATION "\xee\x92\xab" // U+e4ab +#define ICON_FA_ANCHOR_CIRCLE_XMARK "\xee\x92\xac" // U+e4ac +#define ICON_FA_ANCHOR_LOCK "\xee\x92\xad" // U+e4ad +#define ICON_FA_ANGLE_DOWN "\xef\x84\x87" // U+f107 +#define ICON_FA_ANGLE_LEFT "\xef\x84\x84" // U+f104 +#define ICON_FA_ANGLE_RIGHT "\xef\x84\x85" // U+f105 +#define ICON_FA_ANGLE_UP "\xef\x84\x86" // U+f106 +#define ICON_FA_ANGLES_DOWN "\xef\x84\x83" // U+f103 +#define ICON_FA_ANGLES_LEFT "\xef\x84\x80" // U+f100 +#define ICON_FA_ANGLES_RIGHT "\xef\x84\x81" // U+f101 +#define ICON_FA_ANGLES_UP "\xef\x84\x82" // U+f102 +#define ICON_FA_ANKH "\xef\x99\x84" // U+f644 +#define ICON_FA_APPLE_WHOLE "\xef\x97\x91" // U+f5d1 +#define ICON_FA_ARCHWAY "\xef\x95\x97" // U+f557 +#define ICON_FA_ARROW_DOWN "\xef\x81\xa3" // U+f063 +#define ICON_FA_ARROW_DOWN_1_9 "\xef\x85\xa2" // U+f162 +#define ICON_FA_ARROW_DOWN_9_1 "\xef\xa2\x86" // U+f886 +#define ICON_FA_ARROW_DOWN_A_Z "\xef\x85\x9d" // U+f15d +#define ICON_FA_ARROW_DOWN_LONG "\xef\x85\xb5" // U+f175 +#define ICON_FA_ARROW_DOWN_SHORT_WIDE "\xef\xa2\x84" // U+f884 +#define ICON_FA_ARROW_DOWN_UP_ACROSS_LINE "\xee\x92\xaf" // U+e4af +#define ICON_FA_ARROW_DOWN_UP_LOCK "\xee\x92\xb0" // U+e4b0 +#define ICON_FA_ARROW_DOWN_WIDE_SHORT "\xef\x85\xa0" // U+f160 +#define ICON_FA_ARROW_DOWN_Z_A "\xef\xa2\x81" // U+f881 +#define ICON_FA_ARROW_LEFT "\xef\x81\xa0" // U+f060 +#define ICON_FA_ARROW_LEFT_LONG "\xef\x85\xb7" // U+f177 +#define ICON_FA_ARROW_POINTER "\xef\x89\x85" // U+f245 +#define ICON_FA_ARROW_RIGHT "\xef\x81\xa1" // U+f061 +#define ICON_FA_ARROW_RIGHT_ARROW_LEFT "\xef\x83\xac" // U+f0ec +#define ICON_FA_ARROW_RIGHT_FROM_BRACKET "\xef\x82\x8b" // U+f08b +#define ICON_FA_ARROW_RIGHT_LONG "\xef\x85\xb8" // U+f178 +#define ICON_FA_ARROW_RIGHT_TO_BRACKET "\xef\x82\x90" // U+f090 +#define ICON_FA_ARROW_RIGHT_TO_CITY "\xee\x92\xb3" // U+e4b3 +#define ICON_FA_ARROW_ROTATE_LEFT "\xef\x83\xa2" // U+f0e2 +#define ICON_FA_ARROW_ROTATE_RIGHT "\xef\x80\x9e" // U+f01e +#define ICON_FA_ARROW_TREND_DOWN "\xee\x82\x97" // U+e097 +#define ICON_FA_ARROW_TREND_UP "\xee\x82\x98" // U+e098 +#define ICON_FA_ARROW_TURN_DOWN "\xef\x85\x89" // U+f149 +#define ICON_FA_ARROW_TURN_UP "\xef\x85\x88" // U+f148 +#define ICON_FA_ARROW_UP "\xef\x81\xa2" // U+f062 +#define ICON_FA_ARROW_UP_1_9 "\xef\x85\xa3" // U+f163 +#define ICON_FA_ARROW_UP_9_1 "\xef\xa2\x87" // U+f887 +#define ICON_FA_ARROW_UP_A_Z "\xef\x85\x9e" // U+f15e +#define ICON_FA_ARROW_UP_FROM_BRACKET "\xee\x82\x9a" // U+e09a +#define ICON_FA_ARROW_UP_FROM_GROUND_WATER "\xee\x92\xb5" // U+e4b5 +#define ICON_FA_ARROW_UP_FROM_WATER_PUMP "\xee\x92\xb6" // U+e4b6 +#define ICON_FA_ARROW_UP_LONG "\xef\x85\xb6" // U+f176 +#define ICON_FA_ARROW_UP_RIGHT_DOTS "\xee\x92\xb7" // U+e4b7 +#define ICON_FA_ARROW_UP_RIGHT_FROM_SQUARE "\xef\x82\x8e" // U+f08e +#define ICON_FA_ARROW_UP_SHORT_WIDE "\xef\xa2\x85" // U+f885 +#define ICON_FA_ARROW_UP_WIDE_SHORT "\xef\x85\xa1" // U+f161 +#define ICON_FA_ARROW_UP_Z_A "\xef\xa2\x82" // U+f882 +#define ICON_FA_ARROWS_DOWN_TO_LINE "\xee\x92\xb8" // U+e4b8 +#define ICON_FA_ARROWS_DOWN_TO_PEOPLE "\xee\x92\xb9" // U+e4b9 +#define ICON_FA_ARROWS_LEFT_RIGHT "\xef\x81\xbe" // U+f07e +#define ICON_FA_ARROWS_LEFT_RIGHT_TO_LINE "\xee\x92\xba" // U+e4ba +#define ICON_FA_ARROWS_ROTATE "\xef\x80\xa1" // U+f021 +#define ICON_FA_ARROWS_SPIN "\xee\x92\xbb" // U+e4bb +#define ICON_FA_ARROWS_SPLIT_UP_AND_LEFT "\xee\x92\xbc" // U+e4bc +#define ICON_FA_ARROWS_TO_CIRCLE "\xee\x92\xbd" // U+e4bd +#define ICON_FA_ARROWS_TO_DOT "\xee\x92\xbe" // U+e4be +#define ICON_FA_ARROWS_TO_EYE "\xee\x92\xbf" // U+e4bf +#define ICON_FA_ARROWS_TURN_RIGHT "\xee\x93\x80" // U+e4c0 +#define ICON_FA_ARROWS_TURN_TO_DOTS "\xee\x93\x81" // U+e4c1 +#define ICON_FA_ARROWS_UP_DOWN "\xef\x81\xbd" // U+f07d +#define ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT "\xef\x81\x87" // U+f047 +#define ICON_FA_ARROWS_UP_TO_LINE "\xee\x93\x82" // U+e4c2 +#define ICON_FA_ASTERISK "*" // U+002a +#define ICON_FA_AT "@" // U+0040 +#define ICON_FA_ATOM "\xef\x97\x92" // U+f5d2 +#define ICON_FA_AUDIO_DESCRIPTION "\xef\x8a\x9e" // U+f29e +#define ICON_FA_AUSTRAL_SIGN "\xee\x82\xa9" // U+e0a9 +#define ICON_FA_AWARD "\xef\x95\x99" // U+f559 +#define ICON_FA_B "B" // U+0042 +#define ICON_FA_BABY "\xef\x9d\xbc" // U+f77c +#define ICON_FA_BABY_CARRIAGE "\xef\x9d\xbd" // U+f77d +#define ICON_FA_BACKWARD "\xef\x81\x8a" // U+f04a +#define ICON_FA_BACKWARD_FAST "\xef\x81\x89" // U+f049 +#define ICON_FA_BACKWARD_STEP "\xef\x81\x88" // U+f048 +#define ICON_FA_BACON "\xef\x9f\xa5" // U+f7e5 +#define ICON_FA_BACTERIA "\xee\x81\x99" // U+e059 +#define ICON_FA_BACTERIUM "\xee\x81\x9a" // U+e05a +#define ICON_FA_BAG_SHOPPING "\xef\x8a\x90" // U+f290 +#define ICON_FA_BAHAI "\xef\x99\xa6" // U+f666 +#define ICON_FA_BAHT_SIGN "\xee\x82\xac" // U+e0ac +#define ICON_FA_BAN "\xef\x81\x9e" // U+f05e +#define ICON_FA_BAN_SMOKING "\xef\x95\x8d" // U+f54d +#define ICON_FA_BANDAGE "\xef\x91\xa2" // U+f462 +#define ICON_FA_BANGLADESHI_TAKA_SIGN "\xee\x8b\xa6" // U+e2e6 +#define ICON_FA_BARCODE "\xef\x80\xaa" // U+f02a +#define ICON_FA_BARS "\xef\x83\x89" // U+f0c9 +#define ICON_FA_BARS_PROGRESS "\xef\xa0\xa8" // U+f828 +#define ICON_FA_BARS_STAGGERED "\xef\x95\x90" // U+f550 +#define ICON_FA_BASEBALL "\xef\x90\xb3" // U+f433 +#define ICON_FA_BASEBALL_BAT_BALL "\xef\x90\xb2" // U+f432 +#define ICON_FA_BASKET_SHOPPING "\xef\x8a\x91" // U+f291 +#define ICON_FA_BASKETBALL "\xef\x90\xb4" // U+f434 +#define ICON_FA_BATH "\xef\x8b\x8d" // U+f2cd +#define ICON_FA_BATTERY_EMPTY "\xef\x89\x84" // U+f244 +#define ICON_FA_BATTERY_FULL "\xef\x89\x80" // U+f240 +#define ICON_FA_BATTERY_HALF "\xef\x89\x82" // U+f242 +#define ICON_FA_BATTERY_QUARTER "\xef\x89\x83" // U+f243 +#define ICON_FA_BATTERY_THREE_QUARTERS "\xef\x89\x81" // U+f241 +#define ICON_FA_BED "\xef\x88\xb6" // U+f236 +#define ICON_FA_BED_PULSE "\xef\x92\x87" // U+f487 +#define ICON_FA_BEER_MUG_EMPTY "\xef\x83\xbc" // U+f0fc +#define ICON_FA_BELL "\xef\x83\xb3" // U+f0f3 +#define ICON_FA_BELL_CONCIERGE "\xef\x95\xa2" // U+f562 +#define ICON_FA_BELL_SLASH "\xef\x87\xb6" // U+f1f6 +#define ICON_FA_BEZIER_CURVE "\xef\x95\x9b" // U+f55b +#define ICON_FA_BICYCLE "\xef\x88\x86" // U+f206 +#define ICON_FA_BINOCULARS "\xef\x87\xa5" // U+f1e5 +#define ICON_FA_BIOHAZARD "\xef\x9e\x80" // U+f780 +#define ICON_FA_BITCOIN_SIGN "\xee\x82\xb4" // U+e0b4 +#define ICON_FA_BLENDER "\xef\x94\x97" // U+f517 +#define ICON_FA_BLENDER_PHONE "\xef\x9a\xb6" // U+f6b6 +#define ICON_FA_BLOG "\xef\x9e\x81" // U+f781 +#define ICON_FA_BOLD "\xef\x80\xb2" // U+f032 +#define ICON_FA_BOLT "\xef\x83\xa7" // U+f0e7 +#define ICON_FA_BOLT_LIGHTNING "\xee\x82\xb7" // U+e0b7 +#define ICON_FA_BOMB "\xef\x87\xa2" // U+f1e2 +#define ICON_FA_BONE "\xef\x97\x97" // U+f5d7 +#define ICON_FA_BONG "\xef\x95\x9c" // U+f55c +#define ICON_FA_BOOK "\xef\x80\xad" // U+f02d +#define ICON_FA_BOOK_ATLAS "\xef\x95\x98" // U+f558 +#define ICON_FA_BOOK_BIBLE "\xef\x99\x87" // U+f647 +#define ICON_FA_BOOK_BOOKMARK "\xee\x82\xbb" // U+e0bb +#define ICON_FA_BOOK_JOURNAL_WHILLS "\xef\x99\xaa" // U+f66a +#define ICON_FA_BOOK_MEDICAL "\xef\x9f\xa6" // U+f7e6 +#define ICON_FA_BOOK_OPEN "\xef\x94\x98" // U+f518 +#define ICON_FA_BOOK_OPEN_READER "\xef\x97\x9a" // U+f5da +#define ICON_FA_BOOK_QURAN "\xef\x9a\x87" // U+f687 +#define ICON_FA_BOOK_SKULL "\xef\x9a\xb7" // U+f6b7 +#define ICON_FA_BOOK_TANAKH "\xef\xa0\xa7" // U+f827 +#define ICON_FA_BOOKMARK "\xef\x80\xae" // U+f02e +#define ICON_FA_BORDER_ALL "\xef\xa1\x8c" // U+f84c +#define ICON_FA_BORDER_NONE "\xef\xa1\x90" // U+f850 +#define ICON_FA_BORDER_TOP_LEFT "\xef\xa1\x93" // U+f853 +#define ICON_FA_BORE_HOLE "\xee\x93\x83" // U+e4c3 +#define ICON_FA_BOTTLE_DROPLET "\xee\x93\x84" // U+e4c4 +#define ICON_FA_BOTTLE_WATER "\xee\x93\x85" // U+e4c5 +#define ICON_FA_BOWL_FOOD "\xee\x93\x86" // U+e4c6 +#define ICON_FA_BOWL_RICE "\xee\x8b\xab" // U+e2eb +#define ICON_FA_BOWLING_BALL "\xef\x90\xb6" // U+f436 +#define ICON_FA_BOX "\xef\x91\xa6" // U+f466 +#define ICON_FA_BOX_ARCHIVE "\xef\x86\x87" // U+f187 +#define ICON_FA_BOX_OPEN "\xef\x92\x9e" // U+f49e +#define ICON_FA_BOX_TISSUE "\xee\x81\x9b" // U+e05b +#define ICON_FA_BOXES_PACKING "\xee\x93\x87" // U+e4c7 +#define ICON_FA_BOXES_STACKED "\xef\x91\xa8" // U+f468 +#define ICON_FA_BRAILLE "\xef\x8a\xa1" // U+f2a1 +#define ICON_FA_BRAIN "\xef\x97\x9c" // U+f5dc +#define ICON_FA_BRAZILIAN_REAL_SIGN "\xee\x91\xac" // U+e46c +#define ICON_FA_BREAD_SLICE "\xef\x9f\xac" // U+f7ec +#define ICON_FA_BRIDGE "\xee\x93\x88" // U+e4c8 +#define ICON_FA_BRIDGE_CIRCLE_CHECK "\xee\x93\x89" // U+e4c9 +#define ICON_FA_BRIDGE_CIRCLE_EXCLAMATION "\xee\x93\x8a" // U+e4ca +#define ICON_FA_BRIDGE_CIRCLE_XMARK "\xee\x93\x8b" // U+e4cb +#define ICON_FA_BRIDGE_LOCK "\xee\x93\x8c" // U+e4cc +#define ICON_FA_BRIDGE_WATER "\xee\x93\x8e" // U+e4ce +#define ICON_FA_BRIEFCASE "\xef\x82\xb1" // U+f0b1 +#define ICON_FA_BRIEFCASE_MEDICAL "\xef\x91\xa9" // U+f469 +#define ICON_FA_BROOM "\xef\x94\x9a" // U+f51a +#define ICON_FA_BROOM_BALL "\xef\x91\x98" // U+f458 +#define ICON_FA_BRUSH "\xef\x95\x9d" // U+f55d +#define ICON_FA_BUCKET "\xee\x93\x8f" // U+e4cf +#define ICON_FA_BUG "\xef\x86\x88" // U+f188 +#define ICON_FA_BUG_SLASH "\xee\x92\x90" // U+e490 +#define ICON_FA_BUGS "\xee\x93\x90" // U+e4d0 +#define ICON_FA_BUILDING "\xef\x86\xad" // U+f1ad +#define ICON_FA_BUILDING_CIRCLE_ARROW_RIGHT "\xee\x93\x91" // U+e4d1 +#define ICON_FA_BUILDING_CIRCLE_CHECK "\xee\x93\x92" // U+e4d2 +#define ICON_FA_BUILDING_CIRCLE_EXCLAMATION "\xee\x93\x93" // U+e4d3 +#define ICON_FA_BUILDING_CIRCLE_XMARK "\xee\x93\x94" // U+e4d4 +#define ICON_FA_BUILDING_COLUMNS "\xef\x86\x9c" // U+f19c +#define ICON_FA_BUILDING_FLAG "\xee\x93\x95" // U+e4d5 +#define ICON_FA_BUILDING_LOCK "\xee\x93\x96" // U+e4d6 +#define ICON_FA_BUILDING_NGO "\xee\x93\x97" // U+e4d7 +#define ICON_FA_BUILDING_SHIELD "\xee\x93\x98" // U+e4d8 +#define ICON_FA_BUILDING_UN "\xee\x93\x99" // U+e4d9 +#define ICON_FA_BUILDING_USER "\xee\x93\x9a" // U+e4da +#define ICON_FA_BUILDING_WHEAT "\xee\x93\x9b" // U+e4db +#define ICON_FA_BULLHORN "\xef\x82\xa1" // U+f0a1 +#define ICON_FA_BULLSEYE "\xef\x85\x80" // U+f140 +#define ICON_FA_BURGER "\xef\xa0\x85" // U+f805 +#define ICON_FA_BURST "\xee\x93\x9c" // U+e4dc +#define ICON_FA_BUS "\xef\x88\x87" // U+f207 +#define ICON_FA_BUS_SIMPLE "\xef\x95\x9e" // U+f55e +#define ICON_FA_BUSINESS_TIME "\xef\x99\x8a" // U+f64a +#define ICON_FA_C "C" // U+0043 +#define ICON_FA_CABLE_CAR "\xef\x9f\x9a" // U+f7da +#define ICON_FA_CAKE_CANDLES "\xef\x87\xbd" // U+f1fd +#define ICON_FA_CALCULATOR "\xef\x87\xac" // U+f1ec +#define ICON_FA_CALENDAR "\xef\x84\xb3" // U+f133 +#define ICON_FA_CALENDAR_CHECK "\xef\x89\xb4" // U+f274 +#define ICON_FA_CALENDAR_DAY "\xef\x9e\x83" // U+f783 +#define ICON_FA_CALENDAR_DAYS "\xef\x81\xb3" // U+f073 +#define ICON_FA_CALENDAR_MINUS "\xef\x89\xb2" // U+f272 +#define ICON_FA_CALENDAR_PLUS "\xef\x89\xb1" // U+f271 +#define ICON_FA_CALENDAR_WEEK "\xef\x9e\x84" // U+f784 +#define ICON_FA_CALENDAR_XMARK "\xef\x89\xb3" // U+f273 +#define ICON_FA_CAMERA "\xef\x80\xb0" // U+f030 +#define ICON_FA_CAMERA_RETRO "\xef\x82\x83" // U+f083 +#define ICON_FA_CAMERA_ROTATE "\xee\x83\x98" // U+e0d8 +#define ICON_FA_CAMPGROUND "\xef\x9a\xbb" // U+f6bb +#define ICON_FA_CANDY_CANE "\xef\x9e\x86" // U+f786 +#define ICON_FA_CANNABIS "\xef\x95\x9f" // U+f55f +#define ICON_FA_CAPSULES "\xef\x91\xab" // U+f46b +#define ICON_FA_CAR "\xef\x86\xb9" // U+f1b9 +#define ICON_FA_CAR_BATTERY "\xef\x97\x9f" // U+f5df +#define ICON_FA_CAR_BURST "\xef\x97\xa1" // U+f5e1 +#define ICON_FA_CAR_ON "\xee\x93\x9d" // U+e4dd +#define ICON_FA_CAR_REAR "\xef\x97\x9e" // U+f5de +#define ICON_FA_CAR_SIDE "\xef\x97\xa4" // U+f5e4 +#define ICON_FA_CAR_TUNNEL "\xee\x93\x9e" // U+e4de +#define ICON_FA_CARAVAN "\xef\xa3\xbf" // U+f8ff +#define ICON_FA_CARET_DOWN "\xef\x83\x97" // U+f0d7 +#define ICON_FA_CARET_LEFT "\xef\x83\x99" // U+f0d9 +#define ICON_FA_CARET_RIGHT "\xef\x83\x9a" // U+f0da +#define ICON_FA_CARET_UP "\xef\x83\x98" // U+f0d8 +#define ICON_FA_CARROT "\xef\x9e\x87" // U+f787 +#define ICON_FA_CART_ARROW_DOWN "\xef\x88\x98" // U+f218 +#define ICON_FA_CART_FLATBED "\xef\x91\xb4" // U+f474 +#define ICON_FA_CART_FLATBED_SUITCASE "\xef\x96\x9d" // U+f59d +#define ICON_FA_CART_PLUS "\xef\x88\x97" // U+f217 +#define ICON_FA_CART_SHOPPING "\xef\x81\xba" // U+f07a +#define ICON_FA_CASH_REGISTER "\xef\x9e\x88" // U+f788 +#define ICON_FA_CAT "\xef\x9a\xbe" // U+f6be +#define ICON_FA_CEDI_SIGN "\xee\x83\x9f" // U+e0df +#define ICON_FA_CENT_SIGN "\xee\x8f\xb5" // U+e3f5 +#define ICON_FA_CERTIFICATE "\xef\x82\xa3" // U+f0a3 +#define ICON_FA_CHAIR "\xef\x9b\x80" // U+f6c0 +#define ICON_FA_CHALKBOARD "\xef\x94\x9b" // U+f51b +#define ICON_FA_CHALKBOARD_USER "\xef\x94\x9c" // U+f51c +#define ICON_FA_CHAMPAGNE_GLASSES "\xef\x9e\x9f" // U+f79f +#define ICON_FA_CHARGING_STATION "\xef\x97\xa7" // U+f5e7 +#define ICON_FA_CHART_AREA "\xef\x87\xbe" // U+f1fe +#define ICON_FA_CHART_BAR "\xef\x82\x80" // U+f080 +#define ICON_FA_CHART_COLUMN "\xee\x83\xa3" // U+e0e3 +#define ICON_FA_CHART_DIAGRAM "\xee\x9a\x95" // U+e695 +#define ICON_FA_CHART_GANTT "\xee\x83\xa4" // U+e0e4 +#define ICON_FA_CHART_LINE "\xef\x88\x81" // U+f201 +#define ICON_FA_CHART_PIE "\xef\x88\x80" // U+f200 +#define ICON_FA_CHART_SIMPLE "\xee\x91\xb3" // U+e473 +#define ICON_FA_CHECK "\xef\x80\x8c" // U+f00c +#define ICON_FA_CHECK_DOUBLE "\xef\x95\xa0" // U+f560 +#define ICON_FA_CHECK_TO_SLOT "\xef\x9d\xb2" // U+f772 +#define ICON_FA_CHEESE "\xef\x9f\xaf" // U+f7ef +#define ICON_FA_CHESS "\xef\x90\xb9" // U+f439 +#define ICON_FA_CHESS_BISHOP "\xef\x90\xba" // U+f43a +#define ICON_FA_CHESS_BOARD "\xef\x90\xbc" // U+f43c +#define ICON_FA_CHESS_KING "\xef\x90\xbf" // U+f43f +#define ICON_FA_CHESS_KNIGHT "\xef\x91\x81" // U+f441 +#define ICON_FA_CHESS_PAWN "\xef\x91\x83" // U+f443 +#define ICON_FA_CHESS_QUEEN "\xef\x91\x85" // U+f445 +#define ICON_FA_CHESS_ROOK "\xef\x91\x87" // U+f447 +#define ICON_FA_CHEVRON_DOWN "\xef\x81\xb8" // U+f078 +#define ICON_FA_CHEVRON_LEFT "\xef\x81\x93" // U+f053 +#define ICON_FA_CHEVRON_RIGHT "\xef\x81\x94" // U+f054 +#define ICON_FA_CHEVRON_UP "\xef\x81\xb7" // U+f077 +#define ICON_FA_CHILD "\xef\x86\xae" // U+f1ae +#define ICON_FA_CHILD_COMBATANT "\xee\x93\xa0" // U+e4e0 +#define ICON_FA_CHILD_DRESS "\xee\x96\x9c" // U+e59c +#define ICON_FA_CHILD_REACHING "\xee\x96\x9d" // U+e59d +#define ICON_FA_CHILDREN "\xee\x93\xa1" // U+e4e1 +#define ICON_FA_CHURCH "\xef\x94\x9d" // U+f51d +#define ICON_FA_CIRCLE "\xef\x84\x91" // U+f111 +#define ICON_FA_CIRCLE_ARROW_DOWN "\xef\x82\xab" // U+f0ab +#define ICON_FA_CIRCLE_ARROW_LEFT "\xef\x82\xa8" // U+f0a8 +#define ICON_FA_CIRCLE_ARROW_RIGHT "\xef\x82\xa9" // U+f0a9 +#define ICON_FA_CIRCLE_ARROW_UP "\xef\x82\xaa" // U+f0aa +#define ICON_FA_CIRCLE_CHECK "\xef\x81\x98" // U+f058 +#define ICON_FA_CIRCLE_CHEVRON_DOWN "\xef\x84\xba" // U+f13a +#define ICON_FA_CIRCLE_CHEVRON_LEFT "\xef\x84\xb7" // U+f137 +#define ICON_FA_CIRCLE_CHEVRON_RIGHT "\xef\x84\xb8" // U+f138 +#define ICON_FA_CIRCLE_CHEVRON_UP "\xef\x84\xb9" // U+f139 +#define ICON_FA_CIRCLE_DOLLAR_TO_SLOT "\xef\x92\xb9" // U+f4b9 +#define ICON_FA_CIRCLE_DOT "\xef\x86\x92" // U+f192 +#define ICON_FA_CIRCLE_DOWN "\xef\x8d\x98" // U+f358 +#define ICON_FA_CIRCLE_EXCLAMATION "\xef\x81\xaa" // U+f06a +#define ICON_FA_CIRCLE_H "\xef\x91\xbe" // U+f47e +#define ICON_FA_CIRCLE_HALF_STROKE "\xef\x81\x82" // U+f042 +#define ICON_FA_CIRCLE_INFO "\xef\x81\x9a" // U+f05a +#define ICON_FA_CIRCLE_LEFT "\xef\x8d\x99" // U+f359 +#define ICON_FA_CIRCLE_MINUS "\xef\x81\x96" // U+f056 +#define ICON_FA_CIRCLE_NODES "\xee\x93\xa2" // U+e4e2 +#define ICON_FA_CIRCLE_NOTCH "\xef\x87\x8e" // U+f1ce +#define ICON_FA_CIRCLE_PAUSE "\xef\x8a\x8b" // U+f28b +#define ICON_FA_CIRCLE_PLAY "\xef\x85\x84" // U+f144 +#define ICON_FA_CIRCLE_PLUS "\xef\x81\x95" // U+f055 +#define ICON_FA_CIRCLE_QUESTION "\xef\x81\x99" // U+f059 +#define ICON_FA_CIRCLE_RADIATION "\xef\x9e\xba" // U+f7ba +#define ICON_FA_CIRCLE_RIGHT "\xef\x8d\x9a" // U+f35a +#define ICON_FA_CIRCLE_STOP "\xef\x8a\x8d" // U+f28d +#define ICON_FA_CIRCLE_UP "\xef\x8d\x9b" // U+f35b +#define ICON_FA_CIRCLE_USER "\xef\x8a\xbd" // U+f2bd +#define ICON_FA_CIRCLE_XMARK "\xef\x81\x97" // U+f057 +#define ICON_FA_CITY "\xef\x99\x8f" // U+f64f +#define ICON_FA_CLAPPERBOARD "\xee\x84\xb1" // U+e131 +#define ICON_FA_CLIPBOARD "\xef\x8c\xa8" // U+f328 +#define ICON_FA_CLIPBOARD_CHECK "\xef\x91\xac" // U+f46c +#define ICON_FA_CLIPBOARD_LIST "\xef\x91\xad" // U+f46d +#define ICON_FA_CLIPBOARD_QUESTION "\xee\x93\xa3" // U+e4e3 +#define ICON_FA_CLIPBOARD_USER "\xef\x9f\xb3" // U+f7f3 +#define ICON_FA_CLOCK "\xef\x80\x97" // U+f017 +#define ICON_FA_CLOCK_ROTATE_LEFT "\xef\x87\x9a" // U+f1da +#define ICON_FA_CLONE "\xef\x89\x8d" // U+f24d +#define ICON_FA_CLOSED_CAPTIONING "\xef\x88\x8a" // U+f20a +#define ICON_FA_CLOUD "\xef\x83\x82" // U+f0c2 +#define ICON_FA_CLOUD_ARROW_DOWN "\xef\x83\xad" // U+f0ed +#define ICON_FA_CLOUD_ARROW_UP "\xef\x83\xae" // U+f0ee +#define ICON_FA_CLOUD_BOLT "\xef\x9d\xac" // U+f76c +#define ICON_FA_CLOUD_MEATBALL "\xef\x9c\xbb" // U+f73b +#define ICON_FA_CLOUD_MOON "\xef\x9b\x83" // U+f6c3 +#define ICON_FA_CLOUD_MOON_RAIN "\xef\x9c\xbc" // U+f73c +#define ICON_FA_CLOUD_RAIN "\xef\x9c\xbd" // U+f73d +#define ICON_FA_CLOUD_SHOWERS_HEAVY "\xef\x9d\x80" // U+f740 +#define ICON_FA_CLOUD_SHOWERS_WATER "\xee\x93\xa4" // U+e4e4 +#define ICON_FA_CLOUD_SUN "\xef\x9b\x84" // U+f6c4 +#define ICON_FA_CLOUD_SUN_RAIN "\xef\x9d\x83" // U+f743 +#define ICON_FA_CLOVER "\xee\x84\xb9" // U+e139 +#define ICON_FA_CODE "\xef\x84\xa1" // U+f121 +#define ICON_FA_CODE_BRANCH "\xef\x84\xa6" // U+f126 +#define ICON_FA_CODE_COMMIT "\xef\x8e\x86" // U+f386 +#define ICON_FA_CODE_COMPARE "\xee\x84\xba" // U+e13a +#define ICON_FA_CODE_FORK "\xee\x84\xbb" // U+e13b +#define ICON_FA_CODE_MERGE "\xef\x8e\x87" // U+f387 +#define ICON_FA_CODE_PULL_REQUEST "\xee\x84\xbc" // U+e13c +#define ICON_FA_COINS "\xef\x94\x9e" // U+f51e +#define ICON_FA_COLON_SIGN "\xee\x85\x80" // U+e140 +#define ICON_FA_COMMENT "\xef\x81\xb5" // U+f075 +#define ICON_FA_COMMENT_DOLLAR "\xef\x99\x91" // U+f651 +#define ICON_FA_COMMENT_DOTS "\xef\x92\xad" // U+f4ad +#define ICON_FA_COMMENT_MEDICAL "\xef\x9f\xb5" // U+f7f5 +#define ICON_FA_COMMENT_NODES "\xee\x9a\x96" // U+e696 +#define ICON_FA_COMMENT_SLASH "\xef\x92\xb3" // U+f4b3 +#define ICON_FA_COMMENT_SMS "\xef\x9f\x8d" // U+f7cd +#define ICON_FA_COMMENTS "\xef\x82\x86" // U+f086 +#define ICON_FA_COMMENTS_DOLLAR "\xef\x99\x93" // U+f653 +#define ICON_FA_COMPACT_DISC "\xef\x94\x9f" // U+f51f +#define ICON_FA_COMPASS "\xef\x85\x8e" // U+f14e +#define ICON_FA_COMPASS_DRAFTING "\xef\x95\xa8" // U+f568 +#define ICON_FA_COMPRESS "\xef\x81\xa6" // U+f066 +#define ICON_FA_COMPUTER "\xee\x93\xa5" // U+e4e5 +#define ICON_FA_COMPUTER_MOUSE "\xef\xa3\x8c" // U+f8cc +#define ICON_FA_COOKIE "\xef\x95\xa3" // U+f563 +#define ICON_FA_COOKIE_BITE "\xef\x95\xa4" // U+f564 +#define ICON_FA_COPY "\xef\x83\x85" // U+f0c5 +#define ICON_FA_COPYRIGHT "\xef\x87\xb9" // U+f1f9 +#define ICON_FA_COUCH "\xef\x92\xb8" // U+f4b8 +#define ICON_FA_COW "\xef\x9b\x88" // U+f6c8 +#define ICON_FA_CREDIT_CARD "\xef\x82\x9d" // U+f09d +#define ICON_FA_CROP "\xef\x84\xa5" // U+f125 +#define ICON_FA_CROP_SIMPLE "\xef\x95\xa5" // U+f565 +#define ICON_FA_CROSS "\xef\x99\x94" // U+f654 +#define ICON_FA_CROSSHAIRS "\xef\x81\x9b" // U+f05b +#define ICON_FA_CROW "\xef\x94\xa0" // U+f520 +#define ICON_FA_CROWN "\xef\x94\xa1" // U+f521 +#define ICON_FA_CRUTCH "\xef\x9f\xb7" // U+f7f7 +#define ICON_FA_CRUZEIRO_SIGN "\xee\x85\x92" // U+e152 +#define ICON_FA_CUBE "\xef\x86\xb2" // U+f1b2 +#define ICON_FA_CUBES "\xef\x86\xb3" // U+f1b3 +#define ICON_FA_CUBES_STACKED "\xee\x93\xa6" // U+e4e6 +#define ICON_FA_D "D" // U+0044 +#define ICON_FA_DATABASE "\xef\x87\x80" // U+f1c0 +#define ICON_FA_DELETE_LEFT "\xef\x95\x9a" // U+f55a +#define ICON_FA_DEMOCRAT "\xef\x9d\x87" // U+f747 +#define ICON_FA_DESKTOP "\xef\x8e\x90" // U+f390 +#define ICON_FA_DHARMACHAKRA "\xef\x99\x95" // U+f655 +#define ICON_FA_DIAGRAM_NEXT "\xee\x91\xb6" // U+e476 +#define ICON_FA_DIAGRAM_PREDECESSOR "\xee\x91\xb7" // U+e477 +#define ICON_FA_DIAGRAM_PROJECT "\xef\x95\x82" // U+f542 +#define ICON_FA_DIAGRAM_SUCCESSOR "\xee\x91\xba" // U+e47a +#define ICON_FA_DIAMOND "\xef\x88\x99" // U+f219 +#define ICON_FA_DIAMOND_TURN_RIGHT "\xef\x97\xab" // U+f5eb +#define ICON_FA_DICE "\xef\x94\xa2" // U+f522 +#define ICON_FA_DICE_D20 "\xef\x9b\x8f" // U+f6cf +#define ICON_FA_DICE_D6 "\xef\x9b\x91" // U+f6d1 +#define ICON_FA_DICE_FIVE "\xef\x94\xa3" // U+f523 +#define ICON_FA_DICE_FOUR "\xef\x94\xa4" // U+f524 +#define ICON_FA_DICE_ONE "\xef\x94\xa5" // U+f525 +#define ICON_FA_DICE_SIX "\xef\x94\xa6" // U+f526 +#define ICON_FA_DICE_THREE "\xef\x94\xa7" // U+f527 +#define ICON_FA_DICE_TWO "\xef\x94\xa8" // U+f528 +#define ICON_FA_DISEASE "\xef\x9f\xba" // U+f7fa +#define ICON_FA_DISPLAY "\xee\x85\xa3" // U+e163 +#define ICON_FA_DIVIDE "\xef\x94\xa9" // U+f529 +#define ICON_FA_DNA "\xef\x91\xb1" // U+f471 +#define ICON_FA_DOG "\xef\x9b\x93" // U+f6d3 +#define ICON_FA_DOLLAR_SIGN "$" // U+0024 +#define ICON_FA_DOLLY "\xef\x91\xb2" // U+f472 +#define ICON_FA_DONG_SIGN "\xee\x85\xa9" // U+e169 +#define ICON_FA_DOOR_CLOSED "\xef\x94\xaa" // U+f52a +#define ICON_FA_DOOR_OPEN "\xef\x94\xab" // U+f52b +#define ICON_FA_DOVE "\xef\x92\xba" // U+f4ba +#define ICON_FA_DOWN_LEFT_AND_UP_RIGHT_TO_CENTER "\xef\x90\xa2" // U+f422 +#define ICON_FA_DOWN_LONG "\xef\x8c\x89" // U+f309 +#define ICON_FA_DOWNLOAD "\xef\x80\x99" // U+f019 +#define ICON_FA_DRAGON "\xef\x9b\x95" // U+f6d5 +#define ICON_FA_DRAW_POLYGON "\xef\x97\xae" // U+f5ee +#define ICON_FA_DROPLET "\xef\x81\x83" // U+f043 +#define ICON_FA_DROPLET_SLASH "\xef\x97\x87" // U+f5c7 +#define ICON_FA_DRUM "\xef\x95\xa9" // U+f569 +#define ICON_FA_DRUM_STEELPAN "\xef\x95\xaa" // U+f56a +#define ICON_FA_DRUMSTICK_BITE "\xef\x9b\x97" // U+f6d7 +#define ICON_FA_DUMBBELL "\xef\x91\x8b" // U+f44b +#define ICON_FA_DUMPSTER "\xef\x9e\x93" // U+f793 +#define ICON_FA_DUMPSTER_FIRE "\xef\x9e\x94" // U+f794 +#define ICON_FA_DUNGEON "\xef\x9b\x99" // U+f6d9 +#define ICON_FA_E "E" // U+0045 +#define ICON_FA_EAR_DEAF "\xef\x8a\xa4" // U+f2a4 +#define ICON_FA_EAR_LISTEN "\xef\x8a\xa2" // U+f2a2 +#define ICON_FA_EARTH_AFRICA "\xef\x95\xbc" // U+f57c +#define ICON_FA_EARTH_AMERICAS "\xef\x95\xbd" // U+f57d +#define ICON_FA_EARTH_ASIA "\xef\x95\xbe" // U+f57e +#define ICON_FA_EARTH_EUROPE "\xef\x9e\xa2" // U+f7a2 +#define ICON_FA_EARTH_OCEANIA "\xee\x91\xbb" // U+e47b +#define ICON_FA_EGG "\xef\x9f\xbb" // U+f7fb +#define ICON_FA_EJECT "\xef\x81\x92" // U+f052 +#define ICON_FA_ELEVATOR "\xee\x85\xad" // U+e16d +#define ICON_FA_ELLIPSIS "\xef\x85\x81" // U+f141 +#define ICON_FA_ELLIPSIS_VERTICAL "\xef\x85\x82" // U+f142 +#define ICON_FA_ENVELOPE "\xef\x83\xa0" // U+f0e0 +#define ICON_FA_ENVELOPE_CIRCLE_CHECK "\xee\x93\xa8" // U+e4e8 +#define ICON_FA_ENVELOPE_OPEN "\xef\x8a\xb6" // U+f2b6 +#define ICON_FA_ENVELOPE_OPEN_TEXT "\xef\x99\x98" // U+f658 +#define ICON_FA_ENVELOPES_BULK "\xef\x99\xb4" // U+f674 +#define ICON_FA_EQUALS "=" // U+003d +#define ICON_FA_ERASER "\xef\x84\xad" // U+f12d +#define ICON_FA_ETHERNET "\xef\x9e\x96" // U+f796 +#define ICON_FA_EURO_SIGN "\xef\x85\x93" // U+f153 +#define ICON_FA_EXCLAMATION "!" // U+0021 +#define ICON_FA_EXPAND "\xef\x81\xa5" // U+f065 +#define ICON_FA_EXPLOSION "\xee\x93\xa9" // U+e4e9 +#define ICON_FA_EYE "\xef\x81\xae" // U+f06e +#define ICON_FA_EYE_DROPPER "\xef\x87\xbb" // U+f1fb +#define ICON_FA_EYE_LOW_VISION "\xef\x8a\xa8" // U+f2a8 +#define ICON_FA_EYE_SLASH "\xef\x81\xb0" // U+f070 +#define ICON_FA_F "F" // U+0046 +#define ICON_FA_FACE_ANGRY "\xef\x95\x96" // U+f556 +#define ICON_FA_FACE_DIZZY "\xef\x95\xa7" // U+f567 +#define ICON_FA_FACE_FLUSHED "\xef\x95\xb9" // U+f579 +#define ICON_FA_FACE_FROWN "\xef\x84\x99" // U+f119 +#define ICON_FA_FACE_FROWN_OPEN "\xef\x95\xba" // U+f57a +#define ICON_FA_FACE_GRIMACE "\xef\x95\xbf" // U+f57f +#define ICON_FA_FACE_GRIN "\xef\x96\x80" // U+f580 +#define ICON_FA_FACE_GRIN_BEAM "\xef\x96\x82" // U+f582 +#define ICON_FA_FACE_GRIN_BEAM_SWEAT "\xef\x96\x83" // U+f583 +#define ICON_FA_FACE_GRIN_HEARTS "\xef\x96\x84" // U+f584 +#define ICON_FA_FACE_GRIN_SQUINT "\xef\x96\x85" // U+f585 +#define ICON_FA_FACE_GRIN_SQUINT_TEARS "\xef\x96\x86" // U+f586 +#define ICON_FA_FACE_GRIN_STARS "\xef\x96\x87" // U+f587 +#define ICON_FA_FACE_GRIN_TEARS "\xef\x96\x88" // U+f588 +#define ICON_FA_FACE_GRIN_TONGUE "\xef\x96\x89" // U+f589 +#define ICON_FA_FACE_GRIN_TONGUE_SQUINT "\xef\x96\x8a" // U+f58a +#define ICON_FA_FACE_GRIN_TONGUE_WINK "\xef\x96\x8b" // U+f58b +#define ICON_FA_FACE_GRIN_WIDE "\xef\x96\x81" // U+f581 +#define ICON_FA_FACE_GRIN_WINK "\xef\x96\x8c" // U+f58c +#define ICON_FA_FACE_KISS "\xef\x96\x96" // U+f596 +#define ICON_FA_FACE_KISS_BEAM "\xef\x96\x97" // U+f597 +#define ICON_FA_FACE_KISS_WINK_HEART "\xef\x96\x98" // U+f598 +#define ICON_FA_FACE_LAUGH "\xef\x96\x99" // U+f599 +#define ICON_FA_FACE_LAUGH_BEAM "\xef\x96\x9a" // U+f59a +#define ICON_FA_FACE_LAUGH_SQUINT "\xef\x96\x9b" // U+f59b +#define ICON_FA_FACE_LAUGH_WINK "\xef\x96\x9c" // U+f59c +#define ICON_FA_FACE_MEH "\xef\x84\x9a" // U+f11a +#define ICON_FA_FACE_MEH_BLANK "\xef\x96\xa4" // U+f5a4 +#define ICON_FA_FACE_ROLLING_EYES "\xef\x96\xa5" // U+f5a5 +#define ICON_FA_FACE_SAD_CRY "\xef\x96\xb3" // U+f5b3 +#define ICON_FA_FACE_SAD_TEAR "\xef\x96\xb4" // U+f5b4 +#define ICON_FA_FACE_SMILE "\xef\x84\x98" // U+f118 +#define ICON_FA_FACE_SMILE_BEAM "\xef\x96\xb8" // U+f5b8 +#define ICON_FA_FACE_SMILE_WINK "\xef\x93\x9a" // U+f4da +#define ICON_FA_FACE_SURPRISE "\xef\x97\x82" // U+f5c2 +#define ICON_FA_FACE_TIRED "\xef\x97\x88" // U+f5c8 +#define ICON_FA_FAN "\xef\xa1\xa3" // U+f863 +#define ICON_FA_FAUCET "\xee\x80\x85" // U+e005 +#define ICON_FA_FAUCET_DRIP "\xee\x80\x86" // U+e006 +#define ICON_FA_FAX "\xef\x86\xac" // U+f1ac +#define ICON_FA_FEATHER "\xef\x94\xad" // U+f52d +#define ICON_FA_FEATHER_POINTED "\xef\x95\xab" // U+f56b +#define ICON_FA_FERRY "\xee\x93\xaa" // U+e4ea +#define ICON_FA_FILE "\xef\x85\x9b" // U+f15b +#define ICON_FA_FILE_ARROW_DOWN "\xef\x95\xad" // U+f56d +#define ICON_FA_FILE_ARROW_UP "\xef\x95\xb4" // U+f574 +#define ICON_FA_FILE_AUDIO "\xef\x87\x87" // U+f1c7 +#define ICON_FA_FILE_CIRCLE_CHECK "\xee\x96\xa0" // U+e5a0 +#define ICON_FA_FILE_CIRCLE_EXCLAMATION "\xee\x93\xab" // U+e4eb +#define ICON_FA_FILE_CIRCLE_MINUS "\xee\x93\xad" // U+e4ed +#define ICON_FA_FILE_CIRCLE_PLUS "\xee\x92\x94" // U+e494 +#define ICON_FA_FILE_CIRCLE_QUESTION "\xee\x93\xaf" // U+e4ef +#define ICON_FA_FILE_CIRCLE_XMARK "\xee\x96\xa1" // U+e5a1 +#define ICON_FA_FILE_CODE "\xef\x87\x89" // U+f1c9 +#define ICON_FA_FILE_CONTRACT "\xef\x95\xac" // U+f56c +#define ICON_FA_FILE_CSV "\xef\x9b\x9d" // U+f6dd +#define ICON_FA_FILE_EXCEL "\xef\x87\x83" // U+f1c3 +#define ICON_FA_FILE_EXPORT "\xef\x95\xae" // U+f56e +#define ICON_FA_FILE_FRAGMENT "\xee\x9a\x97" // U+e697 +#define ICON_FA_FILE_HALF_DASHED "\xee\x9a\x98" // U+e698 +#define ICON_FA_FILE_IMAGE "\xef\x87\x85" // U+f1c5 +#define ICON_FA_FILE_IMPORT "\xef\x95\xaf" // U+f56f +#define ICON_FA_FILE_INVOICE "\xef\x95\xb0" // U+f570 +#define ICON_FA_FILE_INVOICE_DOLLAR "\xef\x95\xb1" // U+f571 +#define ICON_FA_FILE_LINES "\xef\x85\x9c" // U+f15c +#define ICON_FA_FILE_MEDICAL "\xef\x91\xb7" // U+f477 +#define ICON_FA_FILE_PDF "\xef\x87\x81" // U+f1c1 +#define ICON_FA_FILE_PEN "\xef\x8c\x9c" // U+f31c +#define ICON_FA_FILE_POWERPOINT "\xef\x87\x84" // U+f1c4 +#define ICON_FA_FILE_PRESCRIPTION "\xef\x95\xb2" // U+f572 +#define ICON_FA_FILE_SHIELD "\xee\x93\xb0" // U+e4f0 +#define ICON_FA_FILE_SIGNATURE "\xef\x95\xb3" // U+f573 +#define ICON_FA_FILE_VIDEO "\xef\x87\x88" // U+f1c8 +#define ICON_FA_FILE_WAVEFORM "\xef\x91\xb8" // U+f478 +#define ICON_FA_FILE_WORD "\xef\x87\x82" // U+f1c2 +#define ICON_FA_FILE_ZIPPER "\xef\x87\x86" // U+f1c6 +#define ICON_FA_FILL "\xef\x95\xb5" // U+f575 +#define ICON_FA_FILL_DRIP "\xef\x95\xb6" // U+f576 +#define ICON_FA_FILM "\xef\x80\x88" // U+f008 +#define ICON_FA_FILTER "\xef\x82\xb0" // U+f0b0 +#define ICON_FA_FILTER_CIRCLE_DOLLAR "\xef\x99\xa2" // U+f662 +#define ICON_FA_FILTER_CIRCLE_XMARK "\xee\x85\xbb" // U+e17b +#define ICON_FA_FINGERPRINT "\xef\x95\xb7" // U+f577 +#define ICON_FA_FIRE "\xef\x81\xad" // U+f06d +#define ICON_FA_FIRE_BURNER "\xee\x93\xb1" // U+e4f1 +#define ICON_FA_FIRE_EXTINGUISHER "\xef\x84\xb4" // U+f134 +#define ICON_FA_FIRE_FLAME_CURVED "\xef\x9f\xa4" // U+f7e4 +#define ICON_FA_FIRE_FLAME_SIMPLE "\xef\x91\xaa" // U+f46a +#define ICON_FA_FISH "\xef\x95\xb8" // U+f578 +#define ICON_FA_FISH_FINS "\xee\x93\xb2" // U+e4f2 +#define ICON_FA_FLAG "\xef\x80\xa4" // U+f024 +#define ICON_FA_FLAG_CHECKERED "\xef\x84\x9e" // U+f11e +#define ICON_FA_FLAG_USA "\xef\x9d\x8d" // U+f74d +#define ICON_FA_FLASK "\xef\x83\x83" // U+f0c3 +#define ICON_FA_FLASK_VIAL "\xee\x93\xb3" // U+e4f3 +#define ICON_FA_FLOPPY_DISK "\xef\x83\x87" // U+f0c7 +#define ICON_FA_FLORIN_SIGN "\xee\x86\x84" // U+e184 +#define ICON_FA_FOLDER "\xef\x81\xbb" // U+f07b +#define ICON_FA_FOLDER_CLOSED "\xee\x86\x85" // U+e185 +#define ICON_FA_FOLDER_MINUS "\xef\x99\x9d" // U+f65d +#define ICON_FA_FOLDER_OPEN "\xef\x81\xbc" // U+f07c +#define ICON_FA_FOLDER_PLUS "\xef\x99\x9e" // U+f65e +#define ICON_FA_FOLDER_TREE "\xef\xa0\x82" // U+f802 +#define ICON_FA_FONT "\xef\x80\xb1" // U+f031 +#define ICON_FA_FONT_AWESOME "\xef\x8a\xb4" // U+f2b4 +#define ICON_FA_FOOTBALL "\xef\x91\x8e" // U+f44e +#define ICON_FA_FORWARD "\xef\x81\x8e" // U+f04e +#define ICON_FA_FORWARD_FAST "\xef\x81\x90" // U+f050 +#define ICON_FA_FORWARD_STEP "\xef\x81\x91" // U+f051 +#define ICON_FA_FRANC_SIGN "\xee\x86\x8f" // U+e18f +#define ICON_FA_FROG "\xef\x94\xae" // U+f52e +#define ICON_FA_FUTBOL "\xef\x87\xa3" // U+f1e3 +#define ICON_FA_G "G" // U+0047 +#define ICON_FA_GAMEPAD "\xef\x84\x9b" // U+f11b +#define ICON_FA_GAS_PUMP "\xef\x94\xaf" // U+f52f +#define ICON_FA_GAUGE "\xef\x98\xa4" // U+f624 +#define ICON_FA_GAUGE_HIGH "\xef\x98\xa5" // U+f625 +#define ICON_FA_GAUGE_SIMPLE "\xef\x98\xa9" // U+f629 +#define ICON_FA_GAUGE_SIMPLE_HIGH "\xef\x98\xaa" // U+f62a +#define ICON_FA_GAVEL "\xef\x83\xa3" // U+f0e3 +#define ICON_FA_GEAR "\xef\x80\x93" // U+f013 +#define ICON_FA_GEARS "\xef\x82\x85" // U+f085 +#define ICON_FA_GEM "\xef\x8e\xa5" // U+f3a5 +#define ICON_FA_GENDERLESS "\xef\x88\xad" // U+f22d +#define ICON_FA_GHOST "\xef\x9b\xa2" // U+f6e2 +#define ICON_FA_GIFT "\xef\x81\xab" // U+f06b +#define ICON_FA_GIFTS "\xef\x9e\x9c" // U+f79c +#define ICON_FA_GLASS_WATER "\xee\x93\xb4" // U+e4f4 +#define ICON_FA_GLASS_WATER_DROPLET "\xee\x93\xb5" // U+e4f5 +#define ICON_FA_GLASSES "\xef\x94\xb0" // U+f530 +#define ICON_FA_GLOBE "\xef\x82\xac" // U+f0ac +#define ICON_FA_GOLF_BALL_TEE "\xef\x91\x90" // U+f450 +#define ICON_FA_GOPURAM "\xef\x99\xa4" // U+f664 +#define ICON_FA_GRADUATION_CAP "\xef\x86\x9d" // U+f19d +#define ICON_FA_GREATER_THAN ">" // U+003e +#define ICON_FA_GREATER_THAN_EQUAL "\xef\x94\xb2" // U+f532 +#define ICON_FA_GRIP "\xef\x96\x8d" // U+f58d +#define ICON_FA_GRIP_LINES "\xef\x9e\xa4" // U+f7a4 +#define ICON_FA_GRIP_LINES_VERTICAL "\xef\x9e\xa5" // U+f7a5 +#define ICON_FA_GRIP_VERTICAL "\xef\x96\x8e" // U+f58e +#define ICON_FA_GROUP_ARROWS_ROTATE "\xee\x93\xb6" // U+e4f6 +#define ICON_FA_GUARANI_SIGN "\xee\x86\x9a" // U+e19a +#define ICON_FA_GUITAR "\xef\x9e\xa6" // U+f7a6 +#define ICON_FA_GUN "\xee\x86\x9b" // U+e19b +#define ICON_FA_H "H" // U+0048 +#define ICON_FA_HAMMER "\xef\x9b\xa3" // U+f6e3 +#define ICON_FA_HAMSA "\xef\x99\xa5" // U+f665 +#define ICON_FA_HAND "\xef\x89\x96" // U+f256 +#define ICON_FA_HAND_BACK_FIST "\xef\x89\x95" // U+f255 +#define ICON_FA_HAND_DOTS "\xef\x91\xa1" // U+f461 +#define ICON_FA_HAND_FIST "\xef\x9b\x9e" // U+f6de +#define ICON_FA_HAND_HOLDING "\xef\x92\xbd" // U+f4bd +#define ICON_FA_HAND_HOLDING_DOLLAR "\xef\x93\x80" // U+f4c0 +#define ICON_FA_HAND_HOLDING_DROPLET "\xef\x93\x81" // U+f4c1 +#define ICON_FA_HAND_HOLDING_HAND "\xee\x93\xb7" // U+e4f7 +#define ICON_FA_HAND_HOLDING_HEART "\xef\x92\xbe" // U+f4be +#define ICON_FA_HAND_HOLDING_MEDICAL "\xee\x81\x9c" // U+e05c +#define ICON_FA_HAND_LIZARD "\xef\x89\x98" // U+f258 +#define ICON_FA_HAND_MIDDLE_FINGER "\xef\xa0\x86" // U+f806 +#define ICON_FA_HAND_PEACE "\xef\x89\x9b" // U+f25b +#define ICON_FA_HAND_POINT_DOWN "\xef\x82\xa7" // U+f0a7 +#define ICON_FA_HAND_POINT_LEFT "\xef\x82\xa5" // U+f0a5 +#define ICON_FA_HAND_POINT_RIGHT "\xef\x82\xa4" // U+f0a4 +#define ICON_FA_HAND_POINT_UP "\xef\x82\xa6" // U+f0a6 +#define ICON_FA_HAND_POINTER "\xef\x89\x9a" // U+f25a +#define ICON_FA_HAND_SCISSORS "\xef\x89\x97" // U+f257 +#define ICON_FA_HAND_SPARKLES "\xee\x81\x9d" // U+e05d +#define ICON_FA_HAND_SPOCK "\xef\x89\x99" // U+f259 +#define ICON_FA_HANDCUFFS "\xee\x93\xb8" // U+e4f8 +#define ICON_FA_HANDS "\xef\x8a\xa7" // U+f2a7 +#define ICON_FA_HANDS_ASL_INTERPRETING "\xef\x8a\xa3" // U+f2a3 +#define ICON_FA_HANDS_BOUND "\xee\x93\xb9" // U+e4f9 +#define ICON_FA_HANDS_BUBBLES "\xee\x81\x9e" // U+e05e +#define ICON_FA_HANDS_CLAPPING "\xee\x86\xa8" // U+e1a8 +#define ICON_FA_HANDS_HOLDING "\xef\x93\x82" // U+f4c2 +#define ICON_FA_HANDS_HOLDING_CHILD "\xee\x93\xba" // U+e4fa +#define ICON_FA_HANDS_HOLDING_CIRCLE "\xee\x93\xbb" // U+e4fb +#define ICON_FA_HANDS_PRAYING "\xef\x9a\x84" // U+f684 +#define ICON_FA_HANDSHAKE "\xef\x8a\xb5" // U+f2b5 +#define ICON_FA_HANDSHAKE_ANGLE "\xef\x93\x84" // U+f4c4 +#define ICON_FA_HANDSHAKE_SIMPLE "\xef\x93\x86" // U+f4c6 +#define ICON_FA_HANDSHAKE_SIMPLE_SLASH "\xee\x81\x9f" // U+e05f +#define ICON_FA_HANDSHAKE_SLASH "\xee\x81\xa0" // U+e060 +#define ICON_FA_HANUKIAH "\xef\x9b\xa6" // U+f6e6 +#define ICON_FA_HARD_DRIVE "\xef\x82\xa0" // U+f0a0 +#define ICON_FA_HASHTAG "#" // U+0023 +#define ICON_FA_HAT_COWBOY "\xef\xa3\x80" // U+f8c0 +#define ICON_FA_HAT_COWBOY_SIDE "\xef\xa3\x81" // U+f8c1 +#define ICON_FA_HAT_WIZARD "\xef\x9b\xa8" // U+f6e8 +#define ICON_FA_HEAD_SIDE_COUGH "\xee\x81\xa1" // U+e061 +#define ICON_FA_HEAD_SIDE_COUGH_SLASH "\xee\x81\xa2" // U+e062 +#define ICON_FA_HEAD_SIDE_MASK "\xee\x81\xa3" // U+e063 +#define ICON_FA_HEAD_SIDE_VIRUS "\xee\x81\xa4" // U+e064 +#define ICON_FA_HEADING "\xef\x87\x9c" // U+f1dc +#define ICON_FA_HEADPHONES "\xef\x80\xa5" // U+f025 +#define ICON_FA_HEADPHONES_SIMPLE "\xef\x96\x8f" // U+f58f +#define ICON_FA_HEADSET "\xef\x96\x90" // U+f590 +#define ICON_FA_HEART "\xef\x80\x84" // U+f004 +#define ICON_FA_HEART_CIRCLE_BOLT "\xee\x93\xbc" // U+e4fc +#define ICON_FA_HEART_CIRCLE_CHECK "\xee\x93\xbd" // U+e4fd +#define ICON_FA_HEART_CIRCLE_EXCLAMATION "\xee\x93\xbe" // U+e4fe +#define ICON_FA_HEART_CIRCLE_MINUS "\xee\x93\xbf" // U+e4ff +#define ICON_FA_HEART_CIRCLE_PLUS "\xee\x94\x80" // U+e500 +#define ICON_FA_HEART_CIRCLE_XMARK "\xee\x94\x81" // U+e501 +#define ICON_FA_HEART_CRACK "\xef\x9e\xa9" // U+f7a9 +#define ICON_FA_HEART_PULSE "\xef\x88\x9e" // U+f21e +#define ICON_FA_HELICOPTER "\xef\x94\xb3" // U+f533 +#define ICON_FA_HELICOPTER_SYMBOL "\xee\x94\x82" // U+e502 +#define ICON_FA_HELMET_SAFETY "\xef\xa0\x87" // U+f807 +#define ICON_FA_HELMET_UN "\xee\x94\x83" // U+e503 +#define ICON_FA_HEXAGON_NODES "\xee\x9a\x99" // U+e699 +#define ICON_FA_HEXAGON_NODES_BOLT "\xee\x9a\x9a" // U+e69a +#define ICON_FA_HIGHLIGHTER "\xef\x96\x91" // U+f591 +#define ICON_FA_HILL_AVALANCHE "\xee\x94\x87" // U+e507 +#define ICON_FA_HILL_ROCKSLIDE "\xee\x94\x88" // U+e508 +#define ICON_FA_HIPPO "\xef\x9b\xad" // U+f6ed +#define ICON_FA_HOCKEY_PUCK "\xef\x91\x93" // U+f453 +#define ICON_FA_HOLLY_BERRY "\xef\x9e\xaa" // U+f7aa +#define ICON_FA_HORSE "\xef\x9b\xb0" // U+f6f0 +#define ICON_FA_HORSE_HEAD "\xef\x9e\xab" // U+f7ab +#define ICON_FA_HOSPITAL "\xef\x83\xb8" // U+f0f8 +#define ICON_FA_HOSPITAL_USER "\xef\xa0\x8d" // U+f80d +#define ICON_FA_HOT_TUB_PERSON "\xef\x96\x93" // U+f593 +#define ICON_FA_HOTDOG "\xef\xa0\x8f" // U+f80f +#define ICON_FA_HOTEL "\xef\x96\x94" // U+f594 +#define ICON_FA_HOURGLASS "\xef\x89\x94" // U+f254 +#define ICON_FA_HOURGLASS_END "\xef\x89\x93" // U+f253 +#define ICON_FA_HOURGLASS_HALF "\xef\x89\x92" // U+f252 +#define ICON_FA_HOURGLASS_START "\xef\x89\x91" // U+f251 +#define ICON_FA_HOUSE "\xef\x80\x95" // U+f015 +#define ICON_FA_HOUSE_CHIMNEY "\xee\x8e\xaf" // U+e3af +#define ICON_FA_HOUSE_CHIMNEY_CRACK "\xef\x9b\xb1" // U+f6f1 +#define ICON_FA_HOUSE_CHIMNEY_MEDICAL "\xef\x9f\xb2" // U+f7f2 +#define ICON_FA_HOUSE_CHIMNEY_USER "\xee\x81\xa5" // U+e065 +#define ICON_FA_HOUSE_CHIMNEY_WINDOW "\xee\x80\x8d" // U+e00d +#define ICON_FA_HOUSE_CIRCLE_CHECK "\xee\x94\x89" // U+e509 +#define ICON_FA_HOUSE_CIRCLE_EXCLAMATION "\xee\x94\x8a" // U+e50a +#define ICON_FA_HOUSE_CIRCLE_XMARK "\xee\x94\x8b" // U+e50b +#define ICON_FA_HOUSE_CRACK "\xee\x8e\xb1" // U+e3b1 +#define ICON_FA_HOUSE_FIRE "\xee\x94\x8c" // U+e50c +#define ICON_FA_HOUSE_FLAG "\xee\x94\x8d" // U+e50d +#define ICON_FA_HOUSE_FLOOD_WATER "\xee\x94\x8e" // U+e50e +#define ICON_FA_HOUSE_FLOOD_WATER_CIRCLE_ARROW_RIGHT "\xee\x94\x8f" // U+e50f +#define ICON_FA_HOUSE_LAPTOP "\xee\x81\xa6" // U+e066 +#define ICON_FA_HOUSE_LOCK "\xee\x94\x90" // U+e510 +#define ICON_FA_HOUSE_MEDICAL "\xee\x8e\xb2" // U+e3b2 +#define ICON_FA_HOUSE_MEDICAL_CIRCLE_CHECK "\xee\x94\x91" // U+e511 +#define ICON_FA_HOUSE_MEDICAL_CIRCLE_EXCLAMATION "\xee\x94\x92" // U+e512 +#define ICON_FA_HOUSE_MEDICAL_CIRCLE_XMARK "\xee\x94\x93" // U+e513 +#define ICON_FA_HOUSE_MEDICAL_FLAG "\xee\x94\x94" // U+e514 +#define ICON_FA_HOUSE_SIGNAL "\xee\x80\x92" // U+e012 +#define ICON_FA_HOUSE_TSUNAMI "\xee\x94\x95" // U+e515 +#define ICON_FA_HOUSE_USER "\xee\x86\xb0" // U+e1b0 +#define ICON_FA_HRYVNIA_SIGN "\xef\x9b\xb2" // U+f6f2 +#define ICON_FA_HURRICANE "\xef\x9d\x91" // U+f751 +#define ICON_FA_I "I" // U+0049 +#define ICON_FA_I_CURSOR "\xef\x89\x86" // U+f246 +#define ICON_FA_ICE_CREAM "\xef\xa0\x90" // U+f810 +#define ICON_FA_ICICLES "\xef\x9e\xad" // U+f7ad +#define ICON_FA_ICONS "\xef\xa1\xad" // U+f86d +#define ICON_FA_ID_BADGE "\xef\x8b\x81" // U+f2c1 +#define ICON_FA_ID_CARD "\xef\x8b\x82" // U+f2c2 +#define ICON_FA_ID_CARD_CLIP "\xef\x91\xbf" // U+f47f +#define ICON_FA_IGLOO "\xef\x9e\xae" // U+f7ae +#define ICON_FA_IMAGE "\xef\x80\xbe" // U+f03e +#define ICON_FA_IMAGE_PORTRAIT "\xef\x8f\xa0" // U+f3e0 +#define ICON_FA_IMAGES "\xef\x8c\x82" // U+f302 +#define ICON_FA_INBOX "\xef\x80\x9c" // U+f01c +#define ICON_FA_INDENT "\xef\x80\xbc" // U+f03c +#define ICON_FA_INDIAN_RUPEE_SIGN "\xee\x86\xbc" // U+e1bc +#define ICON_FA_INDUSTRY "\xef\x89\xb5" // U+f275 +#define ICON_FA_INFINITY "\xef\x94\xb4" // U+f534 +#define ICON_FA_INFO "\xef\x84\xa9" // U+f129 +#define ICON_FA_ITALIC "\xef\x80\xb3" // U+f033 +#define ICON_FA_J "J" // U+004a +#define ICON_FA_JAR "\xee\x94\x96" // U+e516 +#define ICON_FA_JAR_WHEAT "\xee\x94\x97" // U+e517 +#define ICON_FA_JEDI "\xef\x99\xa9" // U+f669 +#define ICON_FA_JET_FIGHTER "\xef\x83\xbb" // U+f0fb +#define ICON_FA_JET_FIGHTER_UP "\xee\x94\x98" // U+e518 +#define ICON_FA_JOINT "\xef\x96\x95" // U+f595 +#define ICON_FA_JUG_DETERGENT "\xee\x94\x99" // U+e519 +#define ICON_FA_K "K" // U+004b +#define ICON_FA_KAABA "\xef\x99\xab" // U+f66b +#define ICON_FA_KEY "\xef\x82\x84" // U+f084 +#define ICON_FA_KEYBOARD "\xef\x84\x9c" // U+f11c +#define ICON_FA_KHANDA "\xef\x99\xad" // U+f66d +#define ICON_FA_KIP_SIGN "\xee\x87\x84" // U+e1c4 +#define ICON_FA_KIT_MEDICAL "\xef\x91\xb9" // U+f479 +#define ICON_FA_KITCHEN_SET "\xee\x94\x9a" // U+e51a +#define ICON_FA_KIWI_BIRD "\xef\x94\xb5" // U+f535 +#define ICON_FA_L "L" // U+004c +#define ICON_FA_LAND_MINE_ON "\xee\x94\x9b" // U+e51b +#define ICON_FA_LANDMARK "\xef\x99\xaf" // U+f66f +#define ICON_FA_LANDMARK_DOME "\xef\x9d\x92" // U+f752 +#define ICON_FA_LANDMARK_FLAG "\xee\x94\x9c" // U+e51c +#define ICON_FA_LANGUAGE "\xef\x86\xab" // U+f1ab +#define ICON_FA_LAPTOP "\xef\x84\x89" // U+f109 +#define ICON_FA_LAPTOP_CODE "\xef\x97\xbc" // U+f5fc +#define ICON_FA_LAPTOP_FILE "\xee\x94\x9d" // U+e51d +#define ICON_FA_LAPTOP_MEDICAL "\xef\xa0\x92" // U+f812 +#define ICON_FA_LARI_SIGN "\xee\x87\x88" // U+e1c8 +#define ICON_FA_LAYER_GROUP "\xef\x97\xbd" // U+f5fd +#define ICON_FA_LEAF "\xef\x81\xac" // U+f06c +#define ICON_FA_LEFT_LONG "\xef\x8c\x8a" // U+f30a +#define ICON_FA_LEFT_RIGHT "\xef\x8c\xb7" // U+f337 +#define ICON_FA_LEMON "\xef\x82\x94" // U+f094 +#define ICON_FA_LESS_THAN "<" // U+003c +#define ICON_FA_LESS_THAN_EQUAL "\xef\x94\xb7" // U+f537 +#define ICON_FA_LIFE_RING "\xef\x87\x8d" // U+f1cd +#define ICON_FA_LIGHTBULB "\xef\x83\xab" // U+f0eb +#define ICON_FA_LINES_LEANING "\xee\x94\x9e" // U+e51e +#define ICON_FA_LINK "\xef\x83\x81" // U+f0c1 +#define ICON_FA_LINK_SLASH "\xef\x84\xa7" // U+f127 +#define ICON_FA_LIRA_SIGN "\xef\x86\x95" // U+f195 +#define ICON_FA_LIST "\xef\x80\xba" // U+f03a +#define ICON_FA_LIST_CHECK "\xef\x82\xae" // U+f0ae +#define ICON_FA_LIST_OL "\xef\x83\x8b" // U+f0cb +#define ICON_FA_LIST_UL "\xef\x83\x8a" // U+f0ca +#define ICON_FA_LITECOIN_SIGN "\xee\x87\x93" // U+e1d3 +#define ICON_FA_LOCATION_ARROW "\xef\x84\xa4" // U+f124 +#define ICON_FA_LOCATION_CROSSHAIRS "\xef\x98\x81" // U+f601 +#define ICON_FA_LOCATION_DOT "\xef\x8f\x85" // U+f3c5 +#define ICON_FA_LOCATION_PIN "\xef\x81\x81" // U+f041 +#define ICON_FA_LOCATION_PIN_LOCK "\xee\x94\x9f" // U+e51f +#define ICON_FA_LOCK "\xef\x80\xa3" // U+f023 +#define ICON_FA_LOCK_OPEN "\xef\x8f\x81" // U+f3c1 +#define ICON_FA_LOCUST "\xee\x94\xa0" // U+e520 +#define ICON_FA_LUNGS "\xef\x98\x84" // U+f604 +#define ICON_FA_LUNGS_VIRUS "\xee\x81\xa7" // U+e067 +#define ICON_FA_M "M" // U+004d +#define ICON_FA_MAGNET "\xef\x81\xb6" // U+f076 +#define ICON_FA_MAGNIFYING_GLASS "\xef\x80\x82" // U+f002 +#define ICON_FA_MAGNIFYING_GLASS_ARROW_RIGHT "\xee\x94\xa1" // U+e521 +#define ICON_FA_MAGNIFYING_GLASS_CHART "\xee\x94\xa2" // U+e522 +#define ICON_FA_MAGNIFYING_GLASS_DOLLAR "\xef\x9a\x88" // U+f688 +#define ICON_FA_MAGNIFYING_GLASS_LOCATION "\xef\x9a\x89" // U+f689 +#define ICON_FA_MAGNIFYING_GLASS_MINUS "\xef\x80\x90" // U+f010 +#define ICON_FA_MAGNIFYING_GLASS_PLUS "\xef\x80\x8e" // U+f00e +#define ICON_FA_MANAT_SIGN "\xee\x87\x95" // U+e1d5 +#define ICON_FA_MAP "\xef\x89\xb9" // U+f279 +#define ICON_FA_MAP_LOCATION "\xef\x96\x9f" // U+f59f +#define ICON_FA_MAP_LOCATION_DOT "\xef\x96\xa0" // U+f5a0 +#define ICON_FA_MAP_PIN "\xef\x89\xb6" // U+f276 +#define ICON_FA_MARKER "\xef\x96\xa1" // U+f5a1 +#define ICON_FA_MARS "\xef\x88\xa2" // U+f222 +#define ICON_FA_MARS_AND_VENUS "\xef\x88\xa4" // U+f224 +#define ICON_FA_MARS_AND_VENUS_BURST "\xee\x94\xa3" // U+e523 +#define ICON_FA_MARS_DOUBLE "\xef\x88\xa7" // U+f227 +#define ICON_FA_MARS_STROKE "\xef\x88\xa9" // U+f229 +#define ICON_FA_MARS_STROKE_RIGHT "\xef\x88\xab" // U+f22b +#define ICON_FA_MARS_STROKE_UP "\xef\x88\xaa" // U+f22a +#define ICON_FA_MARTINI_GLASS "\xef\x95\xbb" // U+f57b +#define ICON_FA_MARTINI_GLASS_CITRUS "\xef\x95\xa1" // U+f561 +#define ICON_FA_MARTINI_GLASS_EMPTY "\xef\x80\x80" // U+f000 +#define ICON_FA_MASK "\xef\x9b\xba" // U+f6fa +#define ICON_FA_MASK_FACE "\xee\x87\x97" // U+e1d7 +#define ICON_FA_MASK_VENTILATOR "\xee\x94\xa4" // U+e524 +#define ICON_FA_MASKS_THEATER "\xef\x98\xb0" // U+f630 +#define ICON_FA_MATTRESS_PILLOW "\xee\x94\xa5" // U+e525 +#define ICON_FA_MAXIMIZE "\xef\x8c\x9e" // U+f31e +#define ICON_FA_MEDAL "\xef\x96\xa2" // U+f5a2 +#define ICON_FA_MEMORY "\xef\x94\xb8" // U+f538 +#define ICON_FA_MENORAH "\xef\x99\xb6" // U+f676 +#define ICON_FA_MERCURY "\xef\x88\xa3" // U+f223 +#define ICON_FA_MESSAGE "\xef\x89\xba" // U+f27a +#define ICON_FA_METEOR "\xef\x9d\x93" // U+f753 +#define ICON_FA_MICROCHIP "\xef\x8b\x9b" // U+f2db +#define ICON_FA_MICROPHONE "\xef\x84\xb0" // U+f130 +#define ICON_FA_MICROPHONE_LINES "\xef\x8f\x89" // U+f3c9 +#define ICON_FA_MICROPHONE_LINES_SLASH "\xef\x94\xb9" // U+f539 +#define ICON_FA_MICROPHONE_SLASH "\xef\x84\xb1" // U+f131 +#define ICON_FA_MICROSCOPE "\xef\x98\x90" // U+f610 +#define ICON_FA_MILL_SIGN "\xee\x87\xad" // U+e1ed +#define ICON_FA_MINIMIZE "\xef\x9e\x8c" // U+f78c +#define ICON_FA_MINUS "\xef\x81\xa8" // U+f068 +#define ICON_FA_MITTEN "\xef\x9e\xb5" // U+f7b5 +#define ICON_FA_MOBILE "\xef\x8f\x8e" // U+f3ce +#define ICON_FA_MOBILE_BUTTON "\xef\x84\x8b" // U+f10b +#define ICON_FA_MOBILE_RETRO "\xee\x94\xa7" // U+e527 +#define ICON_FA_MOBILE_SCREEN "\xef\x8f\x8f" // U+f3cf +#define ICON_FA_MOBILE_SCREEN_BUTTON "\xef\x8f\x8d" // U+f3cd +#define ICON_FA_MONEY_BILL "\xef\x83\x96" // U+f0d6 +#define ICON_FA_MONEY_BILL_1 "\xef\x8f\x91" // U+f3d1 +#define ICON_FA_MONEY_BILL_1_WAVE "\xef\x94\xbb" // U+f53b +#define ICON_FA_MONEY_BILL_TRANSFER "\xee\x94\xa8" // U+e528 +#define ICON_FA_MONEY_BILL_TREND_UP "\xee\x94\xa9" // U+e529 +#define ICON_FA_MONEY_BILL_WAVE "\xef\x94\xba" // U+f53a +#define ICON_FA_MONEY_BILL_WHEAT "\xee\x94\xaa" // U+e52a +#define ICON_FA_MONEY_BILLS "\xee\x87\xb3" // U+e1f3 +#define ICON_FA_MONEY_CHECK "\xef\x94\xbc" // U+f53c +#define ICON_FA_MONEY_CHECK_DOLLAR "\xef\x94\xbd" // U+f53d +#define ICON_FA_MONUMENT "\xef\x96\xa6" // U+f5a6 +#define ICON_FA_MOON "\xef\x86\x86" // U+f186 +#define ICON_FA_MORTAR_PESTLE "\xef\x96\xa7" // U+f5a7 +#define ICON_FA_MOSQUE "\xef\x99\xb8" // U+f678 +#define ICON_FA_MOSQUITO "\xee\x94\xab" // U+e52b +#define ICON_FA_MOSQUITO_NET "\xee\x94\xac" // U+e52c +#define ICON_FA_MOTORCYCLE "\xef\x88\x9c" // U+f21c +#define ICON_FA_MOUND "\xee\x94\xad" // U+e52d +#define ICON_FA_MOUNTAIN "\xef\x9b\xbc" // U+f6fc +#define ICON_FA_MOUNTAIN_CITY "\xee\x94\xae" // U+e52e +#define ICON_FA_MOUNTAIN_SUN "\xee\x94\xaf" // U+e52f +#define ICON_FA_MUG_HOT "\xef\x9e\xb6" // U+f7b6 +#define ICON_FA_MUG_SAUCER "\xef\x83\xb4" // U+f0f4 +#define ICON_FA_MUSIC "\xef\x80\x81" // U+f001 +#define ICON_FA_N "N" // U+004e +#define ICON_FA_NAIRA_SIGN "\xee\x87\xb6" // U+e1f6 +#define ICON_FA_NETWORK_WIRED "\xef\x9b\xbf" // U+f6ff +#define ICON_FA_NEUTER "\xef\x88\xac" // U+f22c +#define ICON_FA_NEWSPAPER "\xef\x87\xaa" // U+f1ea +#define ICON_FA_NOT_EQUAL "\xef\x94\xbe" // U+f53e +#define ICON_FA_NOTDEF "\xee\x87\xbe" // U+e1fe +#define ICON_FA_NOTE_STICKY "\xef\x89\x89" // U+f249 +#define ICON_FA_NOTES_MEDICAL "\xef\x92\x81" // U+f481 +#define ICON_FA_O "O" // U+004f +#define ICON_FA_OBJECT_GROUP "\xef\x89\x87" // U+f247 +#define ICON_FA_OBJECT_UNGROUP "\xef\x89\x88" // U+f248 +#define ICON_FA_OIL_CAN "\xef\x98\x93" // U+f613 +#define ICON_FA_OIL_WELL "\xee\x94\xb2" // U+e532 +#define ICON_FA_OM "\xef\x99\xb9" // U+f679 +#define ICON_FA_OTTER "\xef\x9c\x80" // U+f700 +#define ICON_FA_OUTDENT "\xef\x80\xbb" // U+f03b +#define ICON_FA_P "P" // U+0050 +#define ICON_FA_PAGER "\xef\xa0\x95" // U+f815 +#define ICON_FA_PAINT_ROLLER "\xef\x96\xaa" // U+f5aa +#define ICON_FA_PAINTBRUSH "\xef\x87\xbc" // U+f1fc +#define ICON_FA_PALETTE "\xef\x94\xbf" // U+f53f +#define ICON_FA_PALLET "\xef\x92\x82" // U+f482 +#define ICON_FA_PANORAMA "\xee\x88\x89" // U+e209 +#define ICON_FA_PAPER_PLANE "\xef\x87\x98" // U+f1d8 +#define ICON_FA_PAPERCLIP "\xef\x83\x86" // U+f0c6 +#define ICON_FA_PARACHUTE_BOX "\xef\x93\x8d" // U+f4cd +#define ICON_FA_PARAGRAPH "\xef\x87\x9d" // U+f1dd +#define ICON_FA_PASSPORT "\xef\x96\xab" // U+f5ab +#define ICON_FA_PASTE "\xef\x83\xaa" // U+f0ea +#define ICON_FA_PAUSE "\xef\x81\x8c" // U+f04c +#define ICON_FA_PAW "\xef\x86\xb0" // U+f1b0 +#define ICON_FA_PEACE "\xef\x99\xbc" // U+f67c +#define ICON_FA_PEN "\xef\x8c\x84" // U+f304 +#define ICON_FA_PEN_CLIP "\xef\x8c\x85" // U+f305 +#define ICON_FA_PEN_FANCY "\xef\x96\xac" // U+f5ac +#define ICON_FA_PEN_NIB "\xef\x96\xad" // U+f5ad +#define ICON_FA_PEN_RULER "\xef\x96\xae" // U+f5ae +#define ICON_FA_PEN_TO_SQUARE "\xef\x81\x84" // U+f044 +#define ICON_FA_PENCIL "\xef\x8c\x83" // U+f303 +#define ICON_FA_PEOPLE_ARROWS "\xee\x81\xa8" // U+e068 +#define ICON_FA_PEOPLE_CARRY_BOX "\xef\x93\x8e" // U+f4ce +#define ICON_FA_PEOPLE_GROUP "\xee\x94\xb3" // U+e533 +#define ICON_FA_PEOPLE_LINE "\xee\x94\xb4" // U+e534 +#define ICON_FA_PEOPLE_PULLING "\xee\x94\xb5" // U+e535 +#define ICON_FA_PEOPLE_ROBBERY "\xee\x94\xb6" // U+e536 +#define ICON_FA_PEOPLE_ROOF "\xee\x94\xb7" // U+e537 +#define ICON_FA_PEPPER_HOT "\xef\xa0\x96" // U+f816 +#define ICON_FA_PERCENT "%" // U+0025 +#define ICON_FA_PERSON "\xef\x86\x83" // U+f183 +#define ICON_FA_PERSON_ARROW_DOWN_TO_LINE "\xee\x94\xb8" // U+e538 +#define ICON_FA_PERSON_ARROW_UP_FROM_LINE "\xee\x94\xb9" // U+e539 +#define ICON_FA_PERSON_BIKING "\xef\xa1\x8a" // U+f84a +#define ICON_FA_PERSON_BOOTH "\xef\x9d\x96" // U+f756 +#define ICON_FA_PERSON_BREASTFEEDING "\xee\x94\xba" // U+e53a +#define ICON_FA_PERSON_BURST "\xee\x94\xbb" // U+e53b +#define ICON_FA_PERSON_CANE "\xee\x94\xbc" // U+e53c +#define ICON_FA_PERSON_CHALKBOARD "\xee\x94\xbd" // U+e53d +#define ICON_FA_PERSON_CIRCLE_CHECK "\xee\x94\xbe" // U+e53e +#define ICON_FA_PERSON_CIRCLE_EXCLAMATION "\xee\x94\xbf" // U+e53f +#define ICON_FA_PERSON_CIRCLE_MINUS "\xee\x95\x80" // U+e540 +#define ICON_FA_PERSON_CIRCLE_PLUS "\xee\x95\x81" // U+e541 +#define ICON_FA_PERSON_CIRCLE_QUESTION "\xee\x95\x82" // U+e542 +#define ICON_FA_PERSON_CIRCLE_XMARK "\xee\x95\x83" // U+e543 +#define ICON_FA_PERSON_DIGGING "\xef\xa1\x9e" // U+f85e +#define ICON_FA_PERSON_DOTS_FROM_LINE "\xef\x91\xb0" // U+f470 +#define ICON_FA_PERSON_DRESS "\xef\x86\x82" // U+f182 +#define ICON_FA_PERSON_DRESS_BURST "\xee\x95\x84" // U+e544 +#define ICON_FA_PERSON_DROWNING "\xee\x95\x85" // U+e545 +#define ICON_FA_PERSON_FALLING "\xee\x95\x86" // U+e546 +#define ICON_FA_PERSON_FALLING_BURST "\xee\x95\x87" // U+e547 +#define ICON_FA_PERSON_HALF_DRESS "\xee\x95\x88" // U+e548 +#define ICON_FA_PERSON_HARASSING "\xee\x95\x89" // U+e549 +#define ICON_FA_PERSON_HIKING "\xef\x9b\xac" // U+f6ec +#define ICON_FA_PERSON_MILITARY_POINTING "\xee\x95\x8a" // U+e54a +#define ICON_FA_PERSON_MILITARY_RIFLE "\xee\x95\x8b" // U+e54b +#define ICON_FA_PERSON_MILITARY_TO_PERSON "\xee\x95\x8c" // U+e54c +#define ICON_FA_PERSON_PRAYING "\xef\x9a\x83" // U+f683 +#define ICON_FA_PERSON_PREGNANT "\xee\x8c\x9e" // U+e31e +#define ICON_FA_PERSON_RAYS "\xee\x95\x8d" // U+e54d +#define ICON_FA_PERSON_RIFLE "\xee\x95\x8e" // U+e54e +#define ICON_FA_PERSON_RUNNING "\xef\x9c\x8c" // U+f70c +#define ICON_FA_PERSON_SHELTER "\xee\x95\x8f" // U+e54f +#define ICON_FA_PERSON_SKATING "\xef\x9f\x85" // U+f7c5 +#define ICON_FA_PERSON_SKIING "\xef\x9f\x89" // U+f7c9 +#define ICON_FA_PERSON_SKIING_NORDIC "\xef\x9f\x8a" // U+f7ca +#define ICON_FA_PERSON_SNOWBOARDING "\xef\x9f\x8e" // U+f7ce +#define ICON_FA_PERSON_SWIMMING "\xef\x97\x84" // U+f5c4 +#define ICON_FA_PERSON_THROUGH_WINDOW "\xee\x96\xa9" // U+e5a9 +#define ICON_FA_PERSON_WALKING "\xef\x95\x94" // U+f554 +#define ICON_FA_PERSON_WALKING_ARROW_LOOP_LEFT "\xee\x95\x91" // U+e551 +#define ICON_FA_PERSON_WALKING_ARROW_RIGHT "\xee\x95\x92" // U+e552 +#define ICON_FA_PERSON_WALKING_DASHED_LINE_ARROW_RIGHT "\xee\x95\x93" // U+e553 +#define ICON_FA_PERSON_WALKING_LUGGAGE "\xee\x95\x94" // U+e554 +#define ICON_FA_PERSON_WALKING_WITH_CANE "\xef\x8a\x9d" // U+f29d +#define ICON_FA_PESETA_SIGN "\xee\x88\xa1" // U+e221 +#define ICON_FA_PESO_SIGN "\xee\x88\xa2" // U+e222 +#define ICON_FA_PHONE "\xef\x82\x95" // U+f095 +#define ICON_FA_PHONE_FLIP "\xef\xa1\xb9" // U+f879 +#define ICON_FA_PHONE_SLASH "\xef\x8f\x9d" // U+f3dd +#define ICON_FA_PHONE_VOLUME "\xef\x8a\xa0" // U+f2a0 +#define ICON_FA_PHOTO_FILM "\xef\xa1\xbc" // U+f87c +#define ICON_FA_PIGGY_BANK "\xef\x93\x93" // U+f4d3 +#define ICON_FA_PILLS "\xef\x92\x84" // U+f484 +#define ICON_FA_PIZZA_SLICE "\xef\xa0\x98" // U+f818 +#define ICON_FA_PLACE_OF_WORSHIP "\xef\x99\xbf" // U+f67f +#define ICON_FA_PLANE "\xef\x81\xb2" // U+f072 +#define ICON_FA_PLANE_ARRIVAL "\xef\x96\xaf" // U+f5af +#define ICON_FA_PLANE_CIRCLE_CHECK "\xee\x95\x95" // U+e555 +#define ICON_FA_PLANE_CIRCLE_EXCLAMATION "\xee\x95\x96" // U+e556 +#define ICON_FA_PLANE_CIRCLE_XMARK "\xee\x95\x97" // U+e557 +#define ICON_FA_PLANE_DEPARTURE "\xef\x96\xb0" // U+f5b0 +#define ICON_FA_PLANE_LOCK "\xee\x95\x98" // U+e558 +#define ICON_FA_PLANE_SLASH "\xee\x81\xa9" // U+e069 +#define ICON_FA_PLANE_UP "\xee\x88\xad" // U+e22d +#define ICON_FA_PLANT_WILT "\xee\x96\xaa" // U+e5aa +#define ICON_FA_PLATE_WHEAT "\xee\x95\x9a" // U+e55a +#define ICON_FA_PLAY "\xef\x81\x8b" // U+f04b +#define ICON_FA_PLUG "\xef\x87\xa6" // U+f1e6 +#define ICON_FA_PLUG_CIRCLE_BOLT "\xee\x95\x9b" // U+e55b +#define ICON_FA_PLUG_CIRCLE_CHECK "\xee\x95\x9c" // U+e55c +#define ICON_FA_PLUG_CIRCLE_EXCLAMATION "\xee\x95\x9d" // U+e55d +#define ICON_FA_PLUG_CIRCLE_MINUS "\xee\x95\x9e" // U+e55e +#define ICON_FA_PLUG_CIRCLE_PLUS "\xee\x95\x9f" // U+e55f +#define ICON_FA_PLUG_CIRCLE_XMARK "\xee\x95\xa0" // U+e560 +#define ICON_FA_PLUS "+" // U+002b +#define ICON_FA_PLUS_MINUS "\xee\x90\xbc" // U+e43c +#define ICON_FA_PODCAST "\xef\x8b\x8e" // U+f2ce +#define ICON_FA_POO "\xef\x8b\xbe" // U+f2fe +#define ICON_FA_POO_STORM "\xef\x9d\x9a" // U+f75a +#define ICON_FA_POOP "\xef\x98\x99" // U+f619 +#define ICON_FA_POWER_OFF "\xef\x80\x91" // U+f011 +#define ICON_FA_PRESCRIPTION "\xef\x96\xb1" // U+f5b1 +#define ICON_FA_PRESCRIPTION_BOTTLE "\xef\x92\x85" // U+f485 +#define ICON_FA_PRESCRIPTION_BOTTLE_MEDICAL "\xef\x92\x86" // U+f486 +#define ICON_FA_PRINT "\xef\x80\xaf" // U+f02f +#define ICON_FA_PUMP_MEDICAL "\xee\x81\xaa" // U+e06a +#define ICON_FA_PUMP_SOAP "\xee\x81\xab" // U+e06b +#define ICON_FA_PUZZLE_PIECE "\xef\x84\xae" // U+f12e +#define ICON_FA_Q "Q" // U+0051 +#define ICON_FA_QRCODE "\xef\x80\xa9" // U+f029 +#define ICON_FA_QUESTION "?" // U+003f +#define ICON_FA_QUOTE_LEFT "\xef\x84\x8d" // U+f10d +#define ICON_FA_QUOTE_RIGHT "\xef\x84\x8e" // U+f10e +#define ICON_FA_R "R" // U+0052 +#define ICON_FA_RADIATION "\xef\x9e\xb9" // U+f7b9 +#define ICON_FA_RADIO "\xef\xa3\x97" // U+f8d7 +#define ICON_FA_RAINBOW "\xef\x9d\x9b" // U+f75b +#define ICON_FA_RANKING_STAR "\xee\x95\xa1" // U+e561 +#define ICON_FA_RECEIPT "\xef\x95\x83" // U+f543 +#define ICON_FA_RECORD_VINYL "\xef\xa3\x99" // U+f8d9 +#define ICON_FA_RECTANGLE_AD "\xef\x99\x81" // U+f641 +#define ICON_FA_RECTANGLE_LIST "\xef\x80\xa2" // U+f022 +#define ICON_FA_RECTANGLE_XMARK "\xef\x90\x90" // U+f410 +#define ICON_FA_RECYCLE "\xef\x86\xb8" // U+f1b8 +#define ICON_FA_REGISTERED "\xef\x89\x9d" // U+f25d +#define ICON_FA_REPEAT "\xef\x8d\xa3" // U+f363 +#define ICON_FA_REPLY "\xef\x8f\xa5" // U+f3e5 +#define ICON_FA_REPLY_ALL "\xef\x84\xa2" // U+f122 +#define ICON_FA_REPUBLICAN "\xef\x9d\x9e" // U+f75e +#define ICON_FA_RESTROOM "\xef\x9e\xbd" // U+f7bd +#define ICON_FA_RETWEET "\xef\x81\xb9" // U+f079 +#define ICON_FA_RIBBON "\xef\x93\x96" // U+f4d6 +#define ICON_FA_RIGHT_FROM_BRACKET "\xef\x8b\xb5" // U+f2f5 +#define ICON_FA_RIGHT_LEFT "\xef\x8d\xa2" // U+f362 +#define ICON_FA_RIGHT_LONG "\xef\x8c\x8b" // U+f30b +#define ICON_FA_RIGHT_TO_BRACKET "\xef\x8b\xb6" // U+f2f6 +#define ICON_FA_RING "\xef\x9c\x8b" // U+f70b +#define ICON_FA_ROAD "\xef\x80\x98" // U+f018 +#define ICON_FA_ROAD_BARRIER "\xee\x95\xa2" // U+e562 +#define ICON_FA_ROAD_BRIDGE "\xee\x95\xa3" // U+e563 +#define ICON_FA_ROAD_CIRCLE_CHECK "\xee\x95\xa4" // U+e564 +#define ICON_FA_ROAD_CIRCLE_EXCLAMATION "\xee\x95\xa5" // U+e565 +#define ICON_FA_ROAD_CIRCLE_XMARK "\xee\x95\xa6" // U+e566 +#define ICON_FA_ROAD_LOCK "\xee\x95\xa7" // U+e567 +#define ICON_FA_ROAD_SPIKES "\xee\x95\xa8" // U+e568 +#define ICON_FA_ROBOT "\xef\x95\x84" // U+f544 +#define ICON_FA_ROCKET "\xef\x84\xb5" // U+f135 +#define ICON_FA_ROTATE "\xef\x8b\xb1" // U+f2f1 +#define ICON_FA_ROTATE_LEFT "\xef\x8b\xaa" // U+f2ea +#define ICON_FA_ROTATE_RIGHT "\xef\x8b\xb9" // U+f2f9 +#define ICON_FA_ROUTE "\xef\x93\x97" // U+f4d7 +#define ICON_FA_RSS "\xef\x82\x9e" // U+f09e +#define ICON_FA_RUBLE_SIGN "\xef\x85\x98" // U+f158 +#define ICON_FA_RUG "\xee\x95\xa9" // U+e569 +#define ICON_FA_RULER "\xef\x95\x85" // U+f545 +#define ICON_FA_RULER_COMBINED "\xef\x95\x86" // U+f546 +#define ICON_FA_RULER_HORIZONTAL "\xef\x95\x87" // U+f547 +#define ICON_FA_RULER_VERTICAL "\xef\x95\x88" // U+f548 +#define ICON_FA_RUPEE_SIGN "\xef\x85\x96" // U+f156 +#define ICON_FA_RUPIAH_SIGN "\xee\x88\xbd" // U+e23d +#define ICON_FA_S "S" // U+0053 +#define ICON_FA_SACK_DOLLAR "\xef\xa0\x9d" // U+f81d +#define ICON_FA_SACK_XMARK "\xee\x95\xaa" // U+e56a +#define ICON_FA_SAILBOAT "\xee\x91\x85" // U+e445 +#define ICON_FA_SATELLITE "\xef\x9e\xbf" // U+f7bf +#define ICON_FA_SATELLITE_DISH "\xef\x9f\x80" // U+f7c0 +#define ICON_FA_SCALE_BALANCED "\xef\x89\x8e" // U+f24e +#define ICON_FA_SCALE_UNBALANCED "\xef\x94\x95" // U+f515 +#define ICON_FA_SCALE_UNBALANCED_FLIP "\xef\x94\x96" // U+f516 +#define ICON_FA_SCHOOL "\xef\x95\x89" // U+f549 +#define ICON_FA_SCHOOL_CIRCLE_CHECK "\xee\x95\xab" // U+e56b +#define ICON_FA_SCHOOL_CIRCLE_EXCLAMATION "\xee\x95\xac" // U+e56c +#define ICON_FA_SCHOOL_CIRCLE_XMARK "\xee\x95\xad" // U+e56d +#define ICON_FA_SCHOOL_FLAG "\xee\x95\xae" // U+e56e +#define ICON_FA_SCHOOL_LOCK "\xee\x95\xaf" // U+e56f +#define ICON_FA_SCISSORS "\xef\x83\x84" // U+f0c4 +#define ICON_FA_SCREWDRIVER "\xef\x95\x8a" // U+f54a +#define ICON_FA_SCREWDRIVER_WRENCH "\xef\x9f\x99" // U+f7d9 +#define ICON_FA_SCROLL "\xef\x9c\x8e" // U+f70e +#define ICON_FA_SCROLL_TORAH "\xef\x9a\xa0" // U+f6a0 +#define ICON_FA_SD_CARD "\xef\x9f\x82" // U+f7c2 +#define ICON_FA_SECTION "\xee\x91\x87" // U+e447 +#define ICON_FA_SEEDLING "\xef\x93\x98" // U+f4d8 +#define ICON_FA_SERVER "\xef\x88\xb3" // U+f233 +#define ICON_FA_SHAPES "\xef\x98\x9f" // U+f61f +#define ICON_FA_SHARE "\xef\x81\xa4" // U+f064 +#define ICON_FA_SHARE_FROM_SQUARE "\xef\x85\x8d" // U+f14d +#define ICON_FA_SHARE_NODES "\xef\x87\xa0" // U+f1e0 +#define ICON_FA_SHEET_PLASTIC "\xee\x95\xb1" // U+e571 +#define ICON_FA_SHEKEL_SIGN "\xef\x88\x8b" // U+f20b +#define ICON_FA_SHIELD "\xef\x84\xb2" // U+f132 +#define ICON_FA_SHIELD_CAT "\xee\x95\xb2" // U+e572 +#define ICON_FA_SHIELD_DOG "\xee\x95\xb3" // U+e573 +#define ICON_FA_SHIELD_HALVED "\xef\x8f\xad" // U+f3ed +#define ICON_FA_SHIELD_HEART "\xee\x95\xb4" // U+e574 +#define ICON_FA_SHIELD_VIRUS "\xee\x81\xac" // U+e06c +#define ICON_FA_SHIP "\xef\x88\x9a" // U+f21a +#define ICON_FA_SHIRT "\xef\x95\x93" // U+f553 +#define ICON_FA_SHOE_PRINTS "\xef\x95\x8b" // U+f54b +#define ICON_FA_SHOP "\xef\x95\x8f" // U+f54f +#define ICON_FA_SHOP_LOCK "\xee\x92\xa5" // U+e4a5 +#define ICON_FA_SHOP_SLASH "\xee\x81\xb0" // U+e070 +#define ICON_FA_SHOWER "\xef\x8b\x8c" // U+f2cc +#define ICON_FA_SHRIMP "\xee\x91\x88" // U+e448 +#define ICON_FA_SHUFFLE "\xef\x81\xb4" // U+f074 +#define ICON_FA_SHUTTLE_SPACE "\xef\x86\x97" // U+f197 +#define ICON_FA_SIGN_HANGING "\xef\x93\x99" // U+f4d9 +#define ICON_FA_SIGNAL "\xef\x80\x92" // U+f012 +#define ICON_FA_SIGNATURE "\xef\x96\xb7" // U+f5b7 +#define ICON_FA_SIGNS_POST "\xef\x89\xb7" // U+f277 +#define ICON_FA_SIM_CARD "\xef\x9f\x84" // U+f7c4 +#define ICON_FA_SINK "\xee\x81\xad" // U+e06d +#define ICON_FA_SITEMAP "\xef\x83\xa8" // U+f0e8 +#define ICON_FA_SKULL "\xef\x95\x8c" // U+f54c +#define ICON_FA_SKULL_CROSSBONES "\xef\x9c\x94" // U+f714 +#define ICON_FA_SLASH "\xef\x9c\x95" // U+f715 +#define ICON_FA_SLEIGH "\xef\x9f\x8c" // U+f7cc +#define ICON_FA_SLIDERS "\xef\x87\x9e" // U+f1de +#define ICON_FA_SMOG "\xef\x9d\x9f" // U+f75f +#define ICON_FA_SMOKING "\xef\x92\x8d" // U+f48d +#define ICON_FA_SNOWFLAKE "\xef\x8b\x9c" // U+f2dc +#define ICON_FA_SNOWMAN "\xef\x9f\x90" // U+f7d0 +#define ICON_FA_SNOWPLOW "\xef\x9f\x92" // U+f7d2 +#define ICON_FA_SOAP "\xee\x81\xae" // U+e06e +#define ICON_FA_SOCKS "\xef\x9a\x96" // U+f696 +#define ICON_FA_SOLAR_PANEL "\xef\x96\xba" // U+f5ba +#define ICON_FA_SORT "\xef\x83\x9c" // U+f0dc +#define ICON_FA_SORT_DOWN "\xef\x83\x9d" // U+f0dd +#define ICON_FA_SORT_UP "\xef\x83\x9e" // U+f0de +#define ICON_FA_SPA "\xef\x96\xbb" // U+f5bb +#define ICON_FA_SPAGHETTI_MONSTER_FLYING "\xef\x99\xbb" // U+f67b +#define ICON_FA_SPELL_CHECK "\xef\xa2\x91" // U+f891 +#define ICON_FA_SPIDER "\xef\x9c\x97" // U+f717 +#define ICON_FA_SPINNER "\xef\x84\x90" // U+f110 +#define ICON_FA_SPLOTCH "\xef\x96\xbc" // U+f5bc +#define ICON_FA_SPOON "\xef\x8b\xa5" // U+f2e5 +#define ICON_FA_SPRAY_CAN "\xef\x96\xbd" // U+f5bd +#define ICON_FA_SPRAY_CAN_SPARKLES "\xef\x97\x90" // U+f5d0 +#define ICON_FA_SQUARE "\xef\x83\x88" // U+f0c8 +#define ICON_FA_SQUARE_ARROW_UP_RIGHT "\xef\x85\x8c" // U+f14c +#define ICON_FA_SQUARE_BINARY "\xee\x9a\x9b" // U+e69b +#define ICON_FA_SQUARE_CARET_DOWN "\xef\x85\x90" // U+f150 +#define ICON_FA_SQUARE_CARET_LEFT "\xef\x86\x91" // U+f191 +#define ICON_FA_SQUARE_CARET_RIGHT "\xef\x85\x92" // U+f152 +#define ICON_FA_SQUARE_CARET_UP "\xef\x85\x91" // U+f151 +#define ICON_FA_SQUARE_CHECK "\xef\x85\x8a" // U+f14a +#define ICON_FA_SQUARE_ENVELOPE "\xef\x86\x99" // U+f199 +#define ICON_FA_SQUARE_FULL "\xef\x91\x9c" // U+f45c +#define ICON_FA_SQUARE_H "\xef\x83\xbd" // U+f0fd +#define ICON_FA_SQUARE_MINUS "\xef\x85\x86" // U+f146 +#define ICON_FA_SQUARE_NFI "\xee\x95\xb6" // U+e576 +#define ICON_FA_SQUARE_PARKING "\xef\x95\x80" // U+f540 +#define ICON_FA_SQUARE_PEN "\xef\x85\x8b" // U+f14b +#define ICON_FA_SQUARE_PERSON_CONFINED "\xee\x95\xb7" // U+e577 +#define ICON_FA_SQUARE_PHONE "\xef\x82\x98" // U+f098 +#define ICON_FA_SQUARE_PHONE_FLIP "\xef\xa1\xbb" // U+f87b +#define ICON_FA_SQUARE_PLUS "\xef\x83\xbe" // U+f0fe +#define ICON_FA_SQUARE_POLL_HORIZONTAL "\xef\x9a\x82" // U+f682 +#define ICON_FA_SQUARE_POLL_VERTICAL "\xef\x9a\x81" // U+f681 +#define ICON_FA_SQUARE_ROOT_VARIABLE "\xef\x9a\x98" // U+f698 +#define ICON_FA_SQUARE_RSS "\xef\x85\x83" // U+f143 +#define ICON_FA_SQUARE_SHARE_NODES "\xef\x87\xa1" // U+f1e1 +#define ICON_FA_SQUARE_UP_RIGHT "\xef\x8d\xa0" // U+f360 +#define ICON_FA_SQUARE_VIRUS "\xee\x95\xb8" // U+e578 +#define ICON_FA_SQUARE_XMARK "\xef\x8b\x93" // U+f2d3 +#define ICON_FA_STAFF_SNAKE "\xee\x95\xb9" // U+e579 +#define ICON_FA_STAIRS "\xee\x8a\x89" // U+e289 +#define ICON_FA_STAMP "\xef\x96\xbf" // U+f5bf +#define ICON_FA_STAPLER "\xee\x96\xaf" // U+e5af +#define ICON_FA_STAR "\xef\x80\x85" // U+f005 +#define ICON_FA_STAR_AND_CRESCENT "\xef\x9a\x99" // U+f699 +#define ICON_FA_STAR_HALF "\xef\x82\x89" // U+f089 +#define ICON_FA_STAR_HALF_STROKE "\xef\x97\x80" // U+f5c0 +#define ICON_FA_STAR_OF_DAVID "\xef\x9a\x9a" // U+f69a +#define ICON_FA_STAR_OF_LIFE "\xef\x98\xa1" // U+f621 +#define ICON_FA_STERLING_SIGN "\xef\x85\x94" // U+f154 +#define ICON_FA_STETHOSCOPE "\xef\x83\xb1" // U+f0f1 +#define ICON_FA_STOP "\xef\x81\x8d" // U+f04d +#define ICON_FA_STOPWATCH "\xef\x8b\xb2" // U+f2f2 +#define ICON_FA_STOPWATCH_20 "\xee\x81\xaf" // U+e06f +#define ICON_FA_STORE "\xef\x95\x8e" // U+f54e +#define ICON_FA_STORE_SLASH "\xee\x81\xb1" // U+e071 +#define ICON_FA_STREET_VIEW "\xef\x88\x9d" // U+f21d +#define ICON_FA_STRIKETHROUGH "\xef\x83\x8c" // U+f0cc +#define ICON_FA_STROOPWAFEL "\xef\x95\x91" // U+f551 +#define ICON_FA_SUBSCRIPT "\xef\x84\xac" // U+f12c +#define ICON_FA_SUITCASE "\xef\x83\xb2" // U+f0f2 +#define ICON_FA_SUITCASE_MEDICAL "\xef\x83\xba" // U+f0fa +#define ICON_FA_SUITCASE_ROLLING "\xef\x97\x81" // U+f5c1 +#define ICON_FA_SUN "\xef\x86\x85" // U+f185 +#define ICON_FA_SUN_PLANT_WILT "\xee\x95\xba" // U+e57a +#define ICON_FA_SUPERSCRIPT "\xef\x84\xab" // U+f12b +#define ICON_FA_SWATCHBOOK "\xef\x97\x83" // U+f5c3 +#define ICON_FA_SYNAGOGUE "\xef\x9a\x9b" // U+f69b +#define ICON_FA_SYRINGE "\xef\x92\x8e" // U+f48e +#define ICON_FA_T "T" // U+0054 +#define ICON_FA_TABLE "\xef\x83\x8e" // U+f0ce +#define ICON_FA_TABLE_CELLS "\xef\x80\x8a" // U+f00a +#define ICON_FA_TABLE_CELLS_COLUMN_LOCK "\xee\x99\xb8" // U+e678 +#define ICON_FA_TABLE_CELLS_LARGE "\xef\x80\x89" // U+f009 +#define ICON_FA_TABLE_CELLS_ROW_LOCK "\xee\x99\xba" // U+e67a +#define ICON_FA_TABLE_CELLS_ROW_UNLOCK "\xee\x9a\x91" // U+e691 +#define ICON_FA_TABLE_COLUMNS "\xef\x83\x9b" // U+f0db +#define ICON_FA_TABLE_LIST "\xef\x80\x8b" // U+f00b +#define ICON_FA_TABLE_TENNIS_PADDLE_BALL "\xef\x91\x9d" // U+f45d +#define ICON_FA_TABLET "\xef\x8f\xbb" // U+f3fb +#define ICON_FA_TABLET_BUTTON "\xef\x84\x8a" // U+f10a +#define ICON_FA_TABLET_SCREEN_BUTTON "\xef\x8f\xba" // U+f3fa +#define ICON_FA_TABLETS "\xef\x92\x90" // U+f490 +#define ICON_FA_TACHOGRAPH_DIGITAL "\xef\x95\xa6" // U+f566 +#define ICON_FA_TAG "\xef\x80\xab" // U+f02b +#define ICON_FA_TAGS "\xef\x80\xac" // U+f02c +#define ICON_FA_TAPE "\xef\x93\x9b" // U+f4db +#define ICON_FA_TARP "\xee\x95\xbb" // U+e57b +#define ICON_FA_TARP_DROPLET "\xee\x95\xbc" // U+e57c +#define ICON_FA_TAXI "\xef\x86\xba" // U+f1ba +#define ICON_FA_TEETH "\xef\x98\xae" // U+f62e +#define ICON_FA_TEETH_OPEN "\xef\x98\xaf" // U+f62f +#define ICON_FA_TEMPERATURE_ARROW_DOWN "\xee\x80\xbf" // U+e03f +#define ICON_FA_TEMPERATURE_ARROW_UP "\xee\x81\x80" // U+e040 +#define ICON_FA_TEMPERATURE_EMPTY "\xef\x8b\x8b" // U+f2cb +#define ICON_FA_TEMPERATURE_FULL "\xef\x8b\x87" // U+f2c7 +#define ICON_FA_TEMPERATURE_HALF "\xef\x8b\x89" // U+f2c9 +#define ICON_FA_TEMPERATURE_HIGH "\xef\x9d\xa9" // U+f769 +#define ICON_FA_TEMPERATURE_LOW "\xef\x9d\xab" // U+f76b +#define ICON_FA_TEMPERATURE_QUARTER "\xef\x8b\x8a" // U+f2ca +#define ICON_FA_TEMPERATURE_THREE_QUARTERS "\xef\x8b\x88" // U+f2c8 +#define ICON_FA_TENGE_SIGN "\xef\x9f\x97" // U+f7d7 +#define ICON_FA_TENT "\xee\x95\xbd" // U+e57d +#define ICON_FA_TENT_ARROW_DOWN_TO_LINE "\xee\x95\xbe" // U+e57e +#define ICON_FA_TENT_ARROW_LEFT_RIGHT "\xee\x95\xbf" // U+e57f +#define ICON_FA_TENT_ARROW_TURN_LEFT "\xee\x96\x80" // U+e580 +#define ICON_FA_TENT_ARROWS_DOWN "\xee\x96\x81" // U+e581 +#define ICON_FA_TENTS "\xee\x96\x82" // U+e582 +#define ICON_FA_TERMINAL "\xef\x84\xa0" // U+f120 +#define ICON_FA_TEXT_HEIGHT "\xef\x80\xb4" // U+f034 +#define ICON_FA_TEXT_SLASH "\xef\xa1\xbd" // U+f87d +#define ICON_FA_TEXT_WIDTH "\xef\x80\xb5" // U+f035 +#define ICON_FA_THERMOMETER "\xef\x92\x91" // U+f491 +#define ICON_FA_THUMBS_DOWN "\xef\x85\xa5" // U+f165 +#define ICON_FA_THUMBS_UP "\xef\x85\xa4" // U+f164 +#define ICON_FA_THUMBTACK "\xef\x82\x8d" // U+f08d +#define ICON_FA_THUMBTACK_SLASH "\xee\x9a\x8f" // U+e68f +#define ICON_FA_TICKET "\xef\x85\x85" // U+f145 +#define ICON_FA_TICKET_SIMPLE "\xef\x8f\xbf" // U+f3ff +#define ICON_FA_TIMELINE "\xee\x8a\x9c" // U+e29c +#define ICON_FA_TOGGLE_OFF "\xef\x88\x84" // U+f204 +#define ICON_FA_TOGGLE_ON "\xef\x88\x85" // U+f205 +#define ICON_FA_TOILET "\xef\x9f\x98" // U+f7d8 +#define ICON_FA_TOILET_PAPER "\xef\x9c\x9e" // U+f71e +#define ICON_FA_TOILET_PAPER_SLASH "\xee\x81\xb2" // U+e072 +#define ICON_FA_TOILET_PORTABLE "\xee\x96\x83" // U+e583 +#define ICON_FA_TOILETS_PORTABLE "\xee\x96\x84" // U+e584 +#define ICON_FA_TOOLBOX "\xef\x95\x92" // U+f552 +#define ICON_FA_TOOTH "\xef\x97\x89" // U+f5c9 +#define ICON_FA_TORII_GATE "\xef\x9a\xa1" // U+f6a1 +#define ICON_FA_TORNADO "\xef\x9d\xaf" // U+f76f +#define ICON_FA_TOWER_BROADCAST "\xef\x94\x99" // U+f519 +#define ICON_FA_TOWER_CELL "\xee\x96\x85" // U+e585 +#define ICON_FA_TOWER_OBSERVATION "\xee\x96\x86" // U+e586 +#define ICON_FA_TRACTOR "\xef\x9c\xa2" // U+f722 +#define ICON_FA_TRADEMARK "\xef\x89\x9c" // U+f25c +#define ICON_FA_TRAFFIC_LIGHT "\xef\x98\xb7" // U+f637 +#define ICON_FA_TRAILER "\xee\x81\x81" // U+e041 +#define ICON_FA_TRAIN "\xef\x88\xb8" // U+f238 +#define ICON_FA_TRAIN_SUBWAY "\xef\x88\xb9" // U+f239 +#define ICON_FA_TRAIN_TRAM "\xee\x96\xb4" // U+e5b4 +#define ICON_FA_TRANSGENDER "\xef\x88\xa5" // U+f225 +#define ICON_FA_TRASH "\xef\x87\xb8" // U+f1f8 +#define ICON_FA_TRASH_ARROW_UP "\xef\xa0\xa9" // U+f829 +#define ICON_FA_TRASH_CAN "\xef\x8b\xad" // U+f2ed +#define ICON_FA_TRASH_CAN_ARROW_UP "\xef\xa0\xaa" // U+f82a +#define ICON_FA_TREE "\xef\x86\xbb" // U+f1bb +#define ICON_FA_TREE_CITY "\xee\x96\x87" // U+e587 +#define ICON_FA_TRIANGLE_EXCLAMATION "\xef\x81\xb1" // U+f071 +#define ICON_FA_TROPHY "\xef\x82\x91" // U+f091 +#define ICON_FA_TROWEL "\xee\x96\x89" // U+e589 +#define ICON_FA_TROWEL_BRICKS "\xee\x96\x8a" // U+e58a +#define ICON_FA_TRUCK "\xef\x83\x91" // U+f0d1 +#define ICON_FA_TRUCK_ARROW_RIGHT "\xee\x96\x8b" // U+e58b +#define ICON_FA_TRUCK_DROPLET "\xee\x96\x8c" // U+e58c +#define ICON_FA_TRUCK_FAST "\xef\x92\x8b" // U+f48b +#define ICON_FA_TRUCK_FIELD "\xee\x96\x8d" // U+e58d +#define ICON_FA_TRUCK_FIELD_UN "\xee\x96\x8e" // U+e58e +#define ICON_FA_TRUCK_FRONT "\xee\x8a\xb7" // U+e2b7 +#define ICON_FA_TRUCK_MEDICAL "\xef\x83\xb9" // U+f0f9 +#define ICON_FA_TRUCK_MONSTER "\xef\x98\xbb" // U+f63b +#define ICON_FA_TRUCK_MOVING "\xef\x93\x9f" // U+f4df +#define ICON_FA_TRUCK_PICKUP "\xef\x98\xbc" // U+f63c +#define ICON_FA_TRUCK_PLANE "\xee\x96\x8f" // U+e58f +#define ICON_FA_TRUCK_RAMP_BOX "\xef\x93\x9e" // U+f4de +#define ICON_FA_TTY "\xef\x87\xa4" // U+f1e4 +#define ICON_FA_TURKISH_LIRA_SIGN "\xee\x8a\xbb" // U+e2bb +#define ICON_FA_TURN_DOWN "\xef\x8e\xbe" // U+f3be +#define ICON_FA_TURN_UP "\xef\x8e\xbf" // U+f3bf +#define ICON_FA_TV "\xef\x89\xac" // U+f26c +#define ICON_FA_U "U" // U+0055 +#define ICON_FA_UMBRELLA "\xef\x83\xa9" // U+f0e9 +#define ICON_FA_UMBRELLA_BEACH "\xef\x97\x8a" // U+f5ca +#define ICON_FA_UNDERLINE "\xef\x83\x8d" // U+f0cd +#define ICON_FA_UNIVERSAL_ACCESS "\xef\x8a\x9a" // U+f29a +#define ICON_FA_UNLOCK "\xef\x82\x9c" // U+f09c +#define ICON_FA_UNLOCK_KEYHOLE "\xef\x84\xbe" // U+f13e +#define ICON_FA_UP_DOWN "\xef\x8c\xb8" // U+f338 +#define ICON_FA_UP_DOWN_LEFT_RIGHT "\xef\x82\xb2" // U+f0b2 +#define ICON_FA_UP_LONG "\xef\x8c\x8c" // U+f30c +#define ICON_FA_UP_RIGHT_AND_DOWN_LEFT_FROM_CENTER "\xef\x90\xa4" // U+f424 +#define ICON_FA_UP_RIGHT_FROM_SQUARE "\xef\x8d\x9d" // U+f35d +#define ICON_FA_UPLOAD "\xef\x82\x93" // U+f093 +#define ICON_FA_USER "\xef\x80\x87" // U+f007 +#define ICON_FA_USER_ASTRONAUT "\xef\x93\xbb" // U+f4fb +#define ICON_FA_USER_CHECK "\xef\x93\xbc" // U+f4fc +#define ICON_FA_USER_CLOCK "\xef\x93\xbd" // U+f4fd +#define ICON_FA_USER_DOCTOR "\xef\x83\xb0" // U+f0f0 +#define ICON_FA_USER_GEAR "\xef\x93\xbe" // U+f4fe +#define ICON_FA_USER_GRADUATE "\xef\x94\x81" // U+f501 +#define ICON_FA_USER_GROUP "\xef\x94\x80" // U+f500 +#define ICON_FA_USER_INJURED "\xef\x9c\xa8" // U+f728 +#define ICON_FA_USER_LARGE "\xef\x90\x86" // U+f406 +#define ICON_FA_USER_LARGE_SLASH "\xef\x93\xba" // U+f4fa +#define ICON_FA_USER_LOCK "\xef\x94\x82" // U+f502 +#define ICON_FA_USER_MINUS "\xef\x94\x83" // U+f503 +#define ICON_FA_USER_NINJA "\xef\x94\x84" // U+f504 +#define ICON_FA_USER_NURSE "\xef\xa0\xaf" // U+f82f +#define ICON_FA_USER_PEN "\xef\x93\xbf" // U+f4ff +#define ICON_FA_USER_PLUS "\xef\x88\xb4" // U+f234 +#define ICON_FA_USER_SECRET "\xef\x88\x9b" // U+f21b +#define ICON_FA_USER_SHIELD "\xef\x94\x85" // U+f505 +#define ICON_FA_USER_SLASH "\xef\x94\x86" // U+f506 +#define ICON_FA_USER_TAG "\xef\x94\x87" // U+f507 +#define ICON_FA_USER_TIE "\xef\x94\x88" // U+f508 +#define ICON_FA_USER_XMARK "\xef\x88\xb5" // U+f235 +#define ICON_FA_USERS "\xef\x83\x80" // U+f0c0 +#define ICON_FA_USERS_BETWEEN_LINES "\xee\x96\x91" // U+e591 +#define ICON_FA_USERS_GEAR "\xef\x94\x89" // U+f509 +#define ICON_FA_USERS_LINE "\xee\x96\x92" // U+e592 +#define ICON_FA_USERS_RAYS "\xee\x96\x93" // U+e593 +#define ICON_FA_USERS_RECTANGLE "\xee\x96\x94" // U+e594 +#define ICON_FA_USERS_SLASH "\xee\x81\xb3" // U+e073 +#define ICON_FA_USERS_VIEWFINDER "\xee\x96\x95" // U+e595 +#define ICON_FA_UTENSILS "\xef\x8b\xa7" // U+f2e7 +#define ICON_FA_V "V" // U+0056 +#define ICON_FA_VAN_SHUTTLE "\xef\x96\xb6" // U+f5b6 +#define ICON_FA_VAULT "\xee\x8b\x85" // U+e2c5 +#define ICON_FA_VECTOR_SQUARE "\xef\x97\x8b" // U+f5cb +#define ICON_FA_VENUS "\xef\x88\xa1" // U+f221 +#define ICON_FA_VENUS_DOUBLE "\xef\x88\xa6" // U+f226 +#define ICON_FA_VENUS_MARS "\xef\x88\xa8" // U+f228 +#define ICON_FA_VEST "\xee\x82\x85" // U+e085 +#define ICON_FA_VEST_PATCHES "\xee\x82\x86" // U+e086 +#define ICON_FA_VIAL "\xef\x92\x92" // U+f492 +#define ICON_FA_VIAL_CIRCLE_CHECK "\xee\x96\x96" // U+e596 +#define ICON_FA_VIAL_VIRUS "\xee\x96\x97" // U+e597 +#define ICON_FA_VIALS "\xef\x92\x93" // U+f493 +#define ICON_FA_VIDEO "\xef\x80\xbd" // U+f03d +#define ICON_FA_VIDEO_SLASH "\xef\x93\xa2" // U+f4e2 +#define ICON_FA_VIHARA "\xef\x9a\xa7" // U+f6a7 +#define ICON_FA_VIRUS "\xee\x81\xb4" // U+e074 +#define ICON_FA_VIRUS_COVID "\xee\x92\xa8" // U+e4a8 +#define ICON_FA_VIRUS_COVID_SLASH "\xee\x92\xa9" // U+e4a9 +#define ICON_FA_VIRUS_SLASH "\xee\x81\xb5" // U+e075 +#define ICON_FA_VIRUSES "\xee\x81\xb6" // U+e076 +#define ICON_FA_VOICEMAIL "\xef\xa2\x97" // U+f897 +#define ICON_FA_VOLCANO "\xef\x9d\xb0" // U+f770 +#define ICON_FA_VOLLEYBALL "\xef\x91\x9f" // U+f45f +#define ICON_FA_VOLUME_HIGH "\xef\x80\xa8" // U+f028 +#define ICON_FA_VOLUME_LOW "\xef\x80\xa7" // U+f027 +#define ICON_FA_VOLUME_OFF "\xef\x80\xa6" // U+f026 +#define ICON_FA_VOLUME_XMARK "\xef\x9a\xa9" // U+f6a9 +#define ICON_FA_VR_CARDBOARD "\xef\x9c\xa9" // U+f729 +#define ICON_FA_W "W" // U+0057 +#define ICON_FA_WALKIE_TALKIE "\xef\xa3\xaf" // U+f8ef +#define ICON_FA_WALLET "\xef\x95\x95" // U+f555 +#define ICON_FA_WAND_MAGIC "\xef\x83\x90" // U+f0d0 +#define ICON_FA_WAND_MAGIC_SPARKLES "\xee\x8b\x8a" // U+e2ca +#define ICON_FA_WAND_SPARKLES "\xef\x9c\xab" // U+f72b +#define ICON_FA_WAREHOUSE "\xef\x92\x94" // U+f494 +#define ICON_FA_WATER "\xef\x9d\xb3" // U+f773 +#define ICON_FA_WATER_LADDER "\xef\x97\x85" // U+f5c5 +#define ICON_FA_WAVE_SQUARE "\xef\xa0\xbe" // U+f83e +#define ICON_FA_WEB_AWESOME "\xee\x9a\x82" // U+e682 +#define ICON_FA_WEIGHT_HANGING "\xef\x97\x8d" // U+f5cd +#define ICON_FA_WEIGHT_SCALE "\xef\x92\x96" // U+f496 +#define ICON_FA_WHEAT_AWN "\xee\x8b\x8d" // U+e2cd +#define ICON_FA_WHEAT_AWN_CIRCLE_EXCLAMATION "\xee\x96\x98" // U+e598 +#define ICON_FA_WHEELCHAIR "\xef\x86\x93" // U+f193 +#define ICON_FA_WHEELCHAIR_MOVE "\xee\x8b\x8e" // U+e2ce +#define ICON_FA_WHISKEY_GLASS "\xef\x9e\xa0" // U+f7a0 +#define ICON_FA_WIFI "\xef\x87\xab" // U+f1eb +#define ICON_FA_WIND "\xef\x9c\xae" // U+f72e +#define ICON_FA_WINDOW_MAXIMIZE "\xef\x8b\x90" // U+f2d0 +#define ICON_FA_WINDOW_MINIMIZE "\xef\x8b\x91" // U+f2d1 +#define ICON_FA_WINDOW_RESTORE "\xef\x8b\x92" // U+f2d2 +#define ICON_FA_WINE_BOTTLE "\xef\x9c\xaf" // U+f72f +#define ICON_FA_WINE_GLASS "\xef\x93\xa3" // U+f4e3 +#define ICON_FA_WINE_GLASS_EMPTY "\xef\x97\x8e" // U+f5ce +#define ICON_FA_WON_SIGN "\xef\x85\x99" // U+f159 +#define ICON_FA_WORM "\xee\x96\x99" // U+e599 +#define ICON_FA_WRENCH "\xef\x82\xad" // U+f0ad +#define ICON_FA_X "X" // U+0058 +#define ICON_FA_X_RAY "\xef\x92\x97" // U+f497 +#define ICON_FA_XMARK "\xef\x80\x8d" // U+f00d +#define ICON_FA_XMARKS_LINES "\xee\x96\x9a" // U+e59a +#define ICON_FA_Y "Y" // U+0059 +#define ICON_FA_YEN_SIGN "\xef\x85\x97" // U+f157 +#define ICON_FA_YIN_YANG "\xef\x9a\xad" // U+f6ad +#define ICON_FA_Z "Z" // U+005a diff --git a/Orbitersdk/include/ModuleAPI.h b/Orbitersdk/include/ModuleAPI.h index 79d1d0f71..4db3e4e31 100644 --- a/Orbitersdk/include/ModuleAPI.h +++ b/Orbitersdk/include/ModuleAPI.h @@ -10,6 +10,7 @@ #ifndef __MODULEAPI_H #define __MODULEAPI_H +#include namespace oapi { @@ -29,7 +30,7 @@ namespace oapi { * \brief Creates a new ModuleNV instance. * \param hDLL DLL library instance handle (see \ref InitModule) */ - ModuleNV (HINSTANCE hDLL); + explicit ModuleNV (HINSTANCE hDLL); /** * \brief Module interface version @@ -95,7 +96,7 @@ namespace oapi { * \brief Creates a new Module instance. * \param hDLL DLL library instance handle (see \ref InitModule) */ - Module (HINSTANCE hDLL); + explicit Module (HINSTANCE hDLL); virtual ~Module(); /** @@ -259,9 +260,7 @@ namespace oapi { * \brief Process mouse events * * Called to offer a mouse event to the module. - * \param event Event type. This is a Windows message identifier (WM_xxx) such as WM_LBUTTONDOWN. - * \param state Keyboard state during mouse event. This corresponds to the WPARAM value passed to - * the window message handler for mouse events (e.g. MK_CONTROL). + * \param event The SDL mouse event. * \param x Mouse x position in render window at event * \param y Mouse y position in render window at event * \return Returning true prevents the event from entering the standard Orbiter processing queue @@ -274,7 +273,7 @@ namespace oapi { * \note Mouse-processing of the Orbiter main menu cannot be blocked. * \sa clbkProcessKeyboardImmediate, clbkProcessKeyboardBuffered */ - virtual bool clbkProcessMouse (UINT event, DWORD state, DWORD x, DWORD y) { return false; } + virtual bool clbkProcessMouse (const SDL_Event &event, DWORD x, DWORD y) { return false; } /** * \brief Process immediate keyboard events diff --git a/Orbitersdk/include/OrbiterAPI.h b/Orbitersdk/include/OrbiterAPI.h index d2f24c2bd..b5390bebf 100644 --- a/Orbitersdk/include/OrbiterAPI.h +++ b/Orbitersdk/include/OrbiterAPI.h @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(_MSC_VER) && (_MSC_VER < 1920 ) // Microsoft Visual Studio Version 2017 and lower #include @@ -355,6 +356,20 @@ typedef struct { #define OAPISURFACE_SHARED 0x1000 ///< Create a shared resource //@} + +/** + * \ingroup defines + * \defgroup notification Notification type + * These constants specified the type of notification to display. + * \sa oapiAddNotification + */ +//@{ +#define OAPINOTIF_SUCCESS 0 ///< Success +#define OAPINOTIF_WARNING 1 ///< Warning +#define OAPINOTIF_ERROR 2 ///< Error +#define OAPINOTIF_INFO 3 ///< Info +//@} + /** * \ingroup defines * \defgroup grpedit Mesh group editing flags @@ -648,6 +663,58 @@ typedef struct { SURFHANDLE tex; ///< particle texture handle (NULL for default) } PARTICLESTREAMSPEC; +/** + * \defgroup imguidialog ImGui dialog + * + * This group defines ImGuiDialog definition. + */ +//@{ +/** + * \brief Base class for defining an ImGui dialog. + */ +class OAPIFUNC ImGuiDialog { + struct ImGuiDefaultSize { + float width; + float height; + }; +public: + /** + * \brief Create an ImGui dialog object. + * \param name Name of the dialog window + * \note This class must by derived from in order to define a custom ImGui dialog + */ + ImGuiDialog(const char *n, ImGuiDefaultSize ds = {350.0,280.0}):name(n),defaultSize(ds) {} + virtual ~ImGuiDialog(); + bool IsActive() { return active; } + void Activate() { active = true; } + virtual void Display(); + void SetHelp(const char *file, const char *topic = NULL) { + helpfile = file; + if(topic) + helptopic = topic; + else + helptopic.clear(); + } + bool HandleHelpButton(); +protected: + /** + * \brief Callback that is executed when the dialog is closed. + * \note The default behavior is to do nothing + */ + virtual void OnClose() {}; + /** + * \brief Callback that is executed when the dialog should be drawn. + * \note Orbiter takes care of doing the ImGui::Begin() / ImGui::End() pair + * required to create the window and handle its visibility. + * \note ImGui documentation is available at https://github.com/ocornut/imgui + */ + virtual void OnDraw() = 0; + bool active = false; + const std::string name; + ImGuiDefaultSize defaultSize; + std::string helpfile; + std::string helptopic; +}; /** * \defgroup locallight Local lighting interface @@ -6033,6 +6100,14 @@ OAPIFUNC bool oapiUnregisterCustomCmd (int cmdId); */ OAPIFUNC HWND oapiOpenDialog (HINSTANCE hDLLInst, int resourceId, DLGPROC msgProc, void *context = 0); + /** + * \brief Open a dialog box specified by an ImGuiDialog object. + * \param dlg pointer to an ImGuiDialog object responsible for drawing the dialog box. + * \note Only one instance of a dialog box can be open at a time. A second call to + * oapiOpenDialog() with the same dialog will do nothing. + */ +OAPIFUNC void oapiOpenDialog (ImGuiDialog *dlg); + /** * \brief Open a dialog box defined as a Windows resource. This version provides additional * functionality compared to oapiOpenDialog(). @@ -6072,10 +6147,44 @@ OAPIFUNC HWND oapiFindDialog (HINSTANCE hDLLInst, int resourceId); */ OAPIFUNC void oapiCloseDialog (HWND hDlg); + /** + * \brief Close a dialog box. + * \param dlg object pointer that was used to open the dialog box. + */ +OAPIFUNC void oapiCloseDialog (ImGuiDialog *dlg); + + /** + * \brief Show a notification. + * \param type Type of notification + * \param title One liner title of the notification + * \param content Content of the notification (possibly multiline) + * \note title and content are copied and can be safely overwritten/freed after the call + */ +OAPIFUNC void oapiAddNotification(int type, const char *title, const char *content = ""); + + /** + * \brief Retreives a texture ID for use with ImGui. + * \param surf surface handle + * \return The value returned can be used where ImGui expects an ImTextureID argument. + */ +OAPIFUNC uint64_t oapiGetImTextureID (SURFHANDLE surf); /** * \brief Retrieves the context pointer of a dialog box which has been defined during the call to oapiOpenDialog(). * \param hDlg dialog window handle * \note This function returns NULL if no context pointer was specified in oapiOpenDialog(). + * \n Typical usage:\n + * \code + * SURFHANDLE surf = m_mfd->GetDisplaySurface(); + * if (surf) { + * ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left + * ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right + * ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint + * ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.0f); + * ImTextureID txt = oapiGetImTextureID(surf); + * ImGui::Image(txt, ImVec2(sz.x, sz.y), uv_min, uv_max, tint_col, border_col); + * } + * \endcode + * */ OAPIFUNC void *oapiGetDialogContext (HWND hDlg); @@ -6799,108 +6908,108 @@ OAPIFUNC void oapiTriggerRedrawArea (int panel_id, int vc_id, int area_id); */ // ====================================================================== //@{ -#define OAPI_KEY_ESCAPE 0x01 ///< Escape key -#define OAPI_KEY_1 0x02 ///< '1' key on main keyboard -#define OAPI_KEY_2 0x03 ///< '2' key on main keyboard -#define OAPI_KEY_3 0x04 ///< '3' key on main keyboard -#define OAPI_KEY_4 0x05 ///< '4' key on main keyboard -#define OAPI_KEY_5 0x06 ///< '5' key on main keyboard -#define OAPI_KEY_6 0x07 ///< '6' key on main keyboard -#define OAPI_KEY_7 0x08 ///< '7' key on main keyboard -#define OAPI_KEY_8 0x09 ///< '8' key on main keyboard -#define OAPI_KEY_9 0x0A ///< '9' key on main keyboard -#define OAPI_KEY_0 0x0B ///< '0' key on main keyboard -#define OAPI_KEY_MINUS 0x0C ///< '-' key on main keyboard -#define OAPI_KEY_EQUALS 0x0D ///< '=' key on main keyboard -#define OAPI_KEY_BACK 0x0E ///< backspace key -#define OAPI_KEY_TAB 0x0F ///< tab key -#define OAPI_KEY_Q 0x10 ///< 'Q' key -#define OAPI_KEY_W 0x11 ///< 'W' key -#define OAPI_KEY_E 0x12 ///< 'E' key -#define OAPI_KEY_R 0x13 ///< 'R' key -#define OAPI_KEY_T 0x14 ///< 'T' key -#define OAPI_KEY_Y 0x15 ///< 'Y' key -#define OAPI_KEY_U 0x16 ///< 'U' key -#define OAPI_KEY_I 0x17 ///< 'I' key -#define OAPI_KEY_O 0x18 ///< 'O' key -#define OAPI_KEY_P 0x19 ///< 'P' key -#define OAPI_KEY_LBRACKET 0x1A ///< '[' (left bracket) key -#define OAPI_KEY_RBRACKET 0x1B ///< ']' (right bracket) key -#define OAPI_KEY_RETURN 0x1C ///< 'Enter' key on main keyboard -#define OAPI_KEY_LCONTROL 0x1D ///< Left 'Ctrl' key -#define OAPI_KEY_A 0x1E ///< 'A' key -#define OAPI_KEY_S 0x1F ///< 'S' key -#define OAPI_KEY_D 0x20 ///< 'D' key -#define OAPI_KEY_F 0x21 ///< 'F' key -#define OAPI_KEY_G 0x22 ///< 'G' key -#define OAPI_KEY_H 0x23 ///< 'H' key -#define OAPI_KEY_J 0x24 ///< 'J' key -#define OAPI_KEY_K 0x25 ///< 'K' key -#define OAPI_KEY_L 0x26 ///< 'L' key -#define OAPI_KEY_SEMICOLON 0x27 ///< ';' (semicolon) key -#define OAPI_KEY_APOSTROPHE 0x28 ///< ' (apostrophe) key -#define OAPI_KEY_GRAVE 0x29 ///< accent grave -#define OAPI_KEY_LSHIFT 0x2A ///< Left 'Shift' key -#define OAPI_KEY_BACKSLASH 0x2B ///< '\' (Backslash) key -#define OAPI_KEY_Z 0x2C ///< 'Z' key -#define OAPI_KEY_X 0x2D ///< 'X' key -#define OAPI_KEY_C 0x2E ///< 'C' key -#define OAPI_KEY_V 0x2F ///< 'V' key -#define OAPI_KEY_B 0x30 ///< 'B' key -#define OAPI_KEY_N 0x31 ///< 'N' key -#define OAPI_KEY_M 0x32 ///< 'M' key -#define OAPI_KEY_COMMA 0x33 ///< ',' (comma) key -#define OAPI_KEY_PERIOD 0x34 ///< '.' key on main keyboard -#define OAPI_KEY_SLASH 0x35 ///< '/' key on main keyboard -#define OAPI_KEY_RSHIFT 0x36 ///< Right 'Shift' key -#define OAPI_KEY_MULTIPLY 0x37 ///< * on numeric keypad -#define OAPI_KEY_LALT 0x38 ///< left Alt -#define OAPI_KEY_SPACE 0x39 ///< 'Space' key -#define OAPI_KEY_CAPITAL 0x3A ///< caps lock key -#define OAPI_KEY_F1 0x3B ///< F1 function key -#define OAPI_KEY_F2 0x3C ///< F2 function key -#define OAPI_KEY_F3 0x3D ///< F3 function key -#define OAPI_KEY_F4 0x3E ///< F4 function key -#define OAPI_KEY_F5 0x3F ///< F5 function key -#define OAPI_KEY_F6 0x40 ///< F6 function key -#define OAPI_KEY_F7 0x41 ///< F7 function key -#define OAPI_KEY_F8 0x42 ///< F8 function key -#define OAPI_KEY_F9 0x43 ///< F9 function key -#define OAPI_KEY_F10 0x44 ///< F10 function key -#define OAPI_KEY_NUMLOCK 0x45 ///< 'Num Lock' key -#define OAPI_KEY_SCROLL 0x46 ///< Scroll lock -#define OAPI_KEY_NUMPAD7 0x47 ///< '7' key on numeric keypad -#define OAPI_KEY_NUMPAD8 0x48 ///< '8' key on numeric keypad -#define OAPI_KEY_NUMPAD9 0x49 ///< '9' key on numeric keypad -#define OAPI_KEY_SUBTRACT 0x4A ///< '-' key on numeric keypad -#define OAPI_KEY_NUMPAD4 0x4B ///< '4' key on numeric keypad -#define OAPI_KEY_NUMPAD5 0x4C ///< '5' key on numeric keypad -#define OAPI_KEY_NUMPAD6 0x4D ///< '6' key on numeric keypad -#define OAPI_KEY_ADD 0x4E ///< '+' key on numeric keypad -#define OAPI_KEY_NUMPAD1 0x4F ///< '1' key on numeric keypad -#define OAPI_KEY_NUMPAD2 0x50 ///< '2' key on numeric keypad -#define OAPI_KEY_NUMPAD3 0x51 ///< '3' key on numeric keypad -#define OAPI_KEY_NUMPAD0 0x52 ///< '0' key on numeric keypad -#define OAPI_KEY_DECIMAL 0x53 ///< '.' key on numeric keypad -#define OAPI_KEY_OEM_102 0x56 ///< | \< \> on UK/German keyboards -#define OAPI_KEY_F11 0x57 ///< F11 function key -#define OAPI_KEY_F12 0x58 ///< F12 function key -#define OAPI_KEY_NUMPADENTER 0x9C ///< Enter on numeric keypad -#define OAPI_KEY_RCONTROL 0x9D ///< right Control key -#define OAPI_KEY_DIVIDE 0xB5 ///< '/' key on numeric keypad -#define OAPI_KEY_SYSRQ 0xB7 ///< SysRq/PrtScn key -#define OAPI_KEY_RALT 0xB8 ///< right Alt -#define OAPI_KEY_PAUSE 0xC5 ///< Break/Pause key -#define OAPI_KEY_HOME 0xC7 ///< Home on cursor keypad -#define OAPI_KEY_UP 0xC8 ///< up-arrow on cursor keypad -#define OAPI_KEY_PRIOR 0xC9 ///< PgUp on cursor keypad -#define OAPI_KEY_LEFT 0xCB ///< left-arrow on cursor keypad -#define OAPI_KEY_RIGHT 0xCD ///< right-arrow on cursor keypad -#define OAPI_KEY_END 0xCF ///< End on cursor keypad -#define OAPI_KEY_DOWN 0xD0 ///< down-arrow on cursor keypad -#define OAPI_KEY_NEXT 0xD1 ///< PgDn on cursor keypad -#define OAPI_KEY_INSERT 0xD2 ///< Insert on cursor keypad -#define OAPI_KEY_DELETE 0xD3 ///< Delete on cursor keypad +#define OAPI_KEY_ESCAPE SDL_SCANCODE_ESCAPE ///< Escape key +#define OAPI_KEY_1 SDL_SCANCODE_1 ///< '1' key on main keyboard +#define OAPI_KEY_2 SDL_SCANCODE_2 ///< '2' key on main keyboard +#define OAPI_KEY_3 SDL_SCANCODE_3 ///< '3' key on main keyboard +#define OAPI_KEY_4 SDL_SCANCODE_4 ///< '4' key on main keyboard +#define OAPI_KEY_5 SDL_SCANCODE_5 ///< '5' key on main keyboard +#define OAPI_KEY_6 SDL_SCANCODE_6 ///< '6' key on main keyboard +#define OAPI_KEY_7 SDL_SCANCODE_7 ///< '7' key on main keyboard +#define OAPI_KEY_8 SDL_SCANCODE_8 ///< '8' key on main keyboard +#define OAPI_KEY_9 SDL_SCANCODE_9 ///< '9' key on main keyboard +#define OAPI_KEY_0 SDL_SCANCODE_0 ///< '0' key on main keyboard +#define OAPI_KEY_MINUS SDL_SCANCODE_MINUS ///< '-' key on main keyboard +#define OAPI_KEY_EQUALS SDL_SCANCODE_EQUALS ///< '=' key on main keyboard +#define OAPI_KEY_BACK SDL_SCANCODE_BACKSPACE ///< backspace key +#define OAPI_KEY_TAB SDL_SCANCODE_TAB ///< tab key +#define OAPI_KEY_Q SDL_SCANCODE_Q ///< 'Q' key +#define OAPI_KEY_W SDL_SCANCODE_W ///< 'W' key +#define OAPI_KEY_E SDL_SCANCODE_E ///< 'E' key +#define OAPI_KEY_R SDL_SCANCODE_R ///< 'R' key +#define OAPI_KEY_T SDL_SCANCODE_T ///< 'T' key +#define OAPI_KEY_Y SDL_SCANCODE_Y ///< 'Y' key +#define OAPI_KEY_U SDL_SCANCODE_U ///< 'U' key +#define OAPI_KEY_I SDL_SCANCODE_I ///< 'I' key +#define OAPI_KEY_O SDL_SCANCODE_O ///< 'O' key +#define OAPI_KEY_P SDL_SCANCODE_P ///< 'P' key +#define OAPI_KEY_LBRACKET SDL_SCANCODE_LEFTBRACKET ///< '[' (left bracket) key +#define OAPI_KEY_RBRACKET SDL_SCANCODE_RIGHTBRACKET ///< ']' (right bracket) key +#define OAPI_KEY_RETURN SDL_SCANCODE_RETURN ///< 'Enter' key on main keyboard +#define OAPI_KEY_LCONTROL SDL_SCANCODE_LCTRL ///< Left 'Ctrl' key +#define OAPI_KEY_A SDL_SCANCODE_A ///< 'A' key +#define OAPI_KEY_S SDL_SCANCODE_S ///< 'S' key +#define OAPI_KEY_D SDL_SCANCODE_D ///< 'D' key +#define OAPI_KEY_F SDL_SCANCODE_F ///< 'F' key +#define OAPI_KEY_G SDL_SCANCODE_G ///< 'G' key +#define OAPI_KEY_H SDL_SCANCODE_H ///< 'H' key +#define OAPI_KEY_J SDL_SCANCODE_J ///< 'J' key +#define OAPI_KEY_K SDL_SCANCODE_K ///< 'K' key +#define OAPI_KEY_L SDL_SCANCODE_L ///< 'L' key +#define OAPI_KEY_SEMICOLON SDL_SCANCODE_SEMICOLON ///< ';' (semicolon) key +#define OAPI_KEY_APOSTROPHE SDL_SCANCODE_APOSTROPHE ///< ' (apostrophe) key +#define OAPI_KEY_GRAVE SDL_SCANCODE_GRAVE ///< accent grave +#define OAPI_KEY_LSHIFT SDL_SCANCODE_LSHIFT ///< Left 'Shift' key +#define OAPI_KEY_BACKSLASH SDL_SCANCODE_BACKSLASH ///< '\' (Backslash) key +#define OAPI_KEY_Z SDL_SCANCODE_Z ///< 'Z' key +#define OAPI_KEY_X SDL_SCANCODE_X ///< 'X' key +#define OAPI_KEY_C SDL_SCANCODE_C ///< 'C' key +#define OAPI_KEY_V SDL_SCANCODE_V ///< 'V' key +#define OAPI_KEY_B SDL_SCANCODE_B ///< 'B' key +#define OAPI_KEY_N SDL_SCANCODE_N ///< 'N' key +#define OAPI_KEY_M SDL_SCANCODE_M ///< 'M' key +#define OAPI_KEY_COMMA SDL_SCANCODE_COMMA ///< ',' (comma) key +#define OAPI_KEY_PERIOD SDL_SCANCODE_PERIOD ///< '.' key on main keyboard +#define OAPI_KEY_SLASH SDL_SCANCODE_SLASH ///< '/' key on main keyboard +#define OAPI_KEY_RSHIFT SDL_SCANCODE_RSHIFT ///< Right 'Shift' key +#define OAPI_KEY_MULTIPLY SDL_SCANCODE_KP_MULTIPLY ///< * on numeric keypad +#define OAPI_KEY_LALT SDL_SCANCODE_LALT ///< left Alt +#define OAPI_KEY_SPACE SDL_SCANCODE_SPACE ///< 'Space' key +#define OAPI_KEY_CAPITAL SDL_SCANCODE_CAPSLOCK ///< caps lock key +#define OAPI_KEY_F1 SDL_SCANCODE_F1 ///< F1 function key +#define OAPI_KEY_F2 SDL_SCANCODE_F2 ///< F2 function key +#define OAPI_KEY_F3 SDL_SCANCODE_F3 ///< F3 function key +#define OAPI_KEY_F4 SDL_SCANCODE_F4 ///< F4 function key +#define OAPI_KEY_F5 SDL_SCANCODE_F5 ///< F5 function key +#define OAPI_KEY_F6 SDL_SCANCODE_F6 ///< F6 function key +#define OAPI_KEY_F7 SDL_SCANCODE_F7 ///< F7 function key +#define OAPI_KEY_F8 SDL_SCANCODE_F8 ///< F8 function key +#define OAPI_KEY_F9 SDL_SCANCODE_F9 ///< F9 function key +#define OAPI_KEY_F10 SDL_SCANCODE_F10 ///< F10 function key +#define OAPI_KEY_NUMLOCK SDL_SCANCODE_NUMLOCKCLEAR ///< 'Num Lock' key +#define OAPI_KEY_SCROLL SDL_SCANCODE_SCROLLLOCK ///< Scroll lock +#define OAPI_KEY_NUMPAD7 SDL_SCANCODE_KP_7 ///< '7' key on numeric keypad +#define OAPI_KEY_NUMPAD8 SDL_SCANCODE_KP_8 ///< '8' key on numeric keypad +#define OAPI_KEY_NUMPAD9 SDL_SCANCODE_KP_9 ///< '9' key on numeric keypad +#define OAPI_KEY_SUBTRACT SDL_SCANCODE_KP_MINUS ///< '-' key on numeric keypad +#define OAPI_KEY_NUMPAD4 SDL_SCANCODE_KP_4 ///< '4' key on numeric keypad +#define OAPI_KEY_NUMPAD5 SDL_SCANCODE_KP_5 ///< '5' key on numeric keypad +#define OAPI_KEY_NUMPAD6 SDL_SCANCODE_KP_6 ///< '6' key on numeric keypad +#define OAPI_KEY_ADD SDL_SCANCODE_KP_PLUS ///< '+' key on numeric keypad +#define OAPI_KEY_NUMPAD1 SDL_SCANCODE_KP_1 ///< '1' key on numeric keypad +#define OAPI_KEY_NUMPAD2 SDL_SCANCODE_KP_2 ///< '2' key on numeric keypad +#define OAPI_KEY_NUMPAD3 SDL_SCANCODE_KP_3 ///< '3' key on numeric keypad +#define OAPI_KEY_NUMPAD0 SDL_SCANCODE_KP_0 ///< '0' key on numeric keypad +#define OAPI_KEY_DECIMAL SDL_SCANCODE_KP_DECIMAL ///< '.' key on numeric keypad +#define OAPI_KEY_OEM_102 SDL_SCANCODE_NONUSBACKSLASH ///< | \< \> on UK/German keyboards +#define OAPI_KEY_F11 SDL_SCANCODE_F11 ///< F11 function key +#define OAPI_KEY_F12 SDL_SCANCODE_F12 ///< F12 function key +#define OAPI_KEY_NUMPADENTER SDL_SCANCODE_KP_ENTER ///< Enter on numeric keypad +#define OAPI_KEY_RCONTROL SDL_SCANCODE_RCTRL ///< right Control key +#define OAPI_KEY_DIVIDE SDL_SCANCODE_KP_DIVIDE ///< '/' key on numeric keypad +#define OAPI_KEY_SYSRQ SDL_SCANCODE_PRINTSCREEN ///< SysRq/PrtScn key +#define OAPI_KEY_RALT SDL_SCANCODE_RALT ///< right Alt +#define OAPI_KEY_PAUSE SDL_SCANCODE_PAUSE ///< Break/Pause key +#define OAPI_KEY_HOME SDL_SCANCODE_HOME ///< Home on cursor keypad +#define OAPI_KEY_UP SDL_SCANCODE_UP ///< up-arrow on cursor keypad +#define OAPI_KEY_PRIOR SDL_SCANCODE_PAGEUP ///< PgUp on cursor keypad +#define OAPI_KEY_LEFT SDL_SCANCODE_LEFT ///< left-arrow on cursor keypad +#define OAPI_KEY_RIGHT SDL_SCANCODE_RIGHT ///< right-arrow on cursor keypad +#define OAPI_KEY_END SDL_SCANCODE_END ///< End on cursor keypad +#define OAPI_KEY_DOWN SDL_SCANCODE_DOWN ///< down-arrow on cursor keypad +#define OAPI_KEY_NEXT SDL_SCANCODE_PAGEDOWN ///< PgDn on cursor keypad +#define OAPI_KEY_INSERT SDL_SCANCODE_INSERT ///< Insert on cursor keypad +#define OAPI_KEY_DELETE SDL_SCANCODE_DELETE ///< Delete on cursor keypad //@} #define KEYDOWN(buf,key) (buf[key] & 0x80) diff --git a/Orbitersdk/include/SDL3/SDL_oapi.h b/Orbitersdk/include/SDL3/SDL_oapi.h new file mode 100644 index 000000000..4c5d78bd1 --- /dev/null +++ b/Orbitersdk/include/SDL3/SDL_oapi.h @@ -0,0 +1,79 @@ +#ifndef SDLWRAPPERS_H +#define SDLWRAPPERS_H + +#include +#include +#include +#include + +namespace sdl { + +/** + * \brief A wrapper for \ref SDL_Window which handles deletion and move. + * + * It is expected to be used with an external graphics API, e.g. OpenGL, Vulkan, + * or DirectX, rather than SDLGPU, hence "unmanaged". + */ +class OAPIFUNC UnmanagedWindow { +public: + UnmanagedWindow(const UnmanagedWindow &) = delete; + + UnmanagedWindow &operator=(const UnmanagedWindow &) = delete; + + UnmanagedWindow(UnmanagedWindow &&other) noexcept : m_inner(other.m_inner) { + other.m_inner = nullptr; + } + + UnmanagedWindow(std::string_view title, int width, int height, + SDL_WindowFlags flags); + + explicit UnmanagedWindow(SDL_Window *window) : m_inner(window) {} + + ~UnmanagedWindow(); + + [[nodiscard]] SDL_Window *Inner() const { return m_inner; } + + [[nodiscard]] HWND Win32Handle() const { + return (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(m_inner), + SDL_PROP_WINDOW_WIN32_HWND_POINTER, + nullptr); + } + +private: + SDL_Window *m_inner = nullptr; +}; + +/** + * \brief A wrapper for \ref SDL_Window and \ref SDL_GPUDevice which handles + * setup, deletion, and move. + * + * It is expected to be used with SDLGPU, hence "managed". + */ +class OAPIFUNC ManagedWindow { +public: + ManagedWindow(const ManagedWindow &) = delete; + + ManagedWindow &operator=(const ManagedWindow &) = delete; + + ManagedWindow(ManagedWindow &&other) noexcept + : m_inner(other.m_inner), m_device(other.m_device) { + other.m_inner = nullptr; + other.m_device = nullptr; + } + + ManagedWindow(std::string_view title, int width, int height, + SDL_WindowFlags flags); + + ~ManagedWindow(); + + [[nodiscard]] SDL_Window *Inner() const { return m_inner; } + [[nodiscard]] SDL_GPUDevice *Device() const { return m_device; } + +private: + SDL_Window *m_inner = nullptr; + SDL_GPUDevice *m_device = nullptr; +}; + +} // namespace sdl + +#endif // SDLWRAPPERS_H diff --git a/Orbitersdk/include/imgui_extras.h b/Orbitersdk/include/imgui_extras.h new file mode 100644 index 000000000..95b0aa86c --- /dev/null +++ b/Orbitersdk/include/imgui_extras.h @@ -0,0 +1,21 @@ +#pragma once + +#include "imgui.h" + +// ImGui extras + +enum class ImGuiFont { + DEFAULT, + MONO +}; + + +namespace ImGui { + + OAPIFUNC bool SliderFloatReset(const char* label, float* v, float v_min, float v_max, float v_default, const char* display_format = "%.3f"); + OAPIFUNC void HelpMarker(const char* desc, bool sameline = true); + OAPIFUNC void BeginGroupPanel(const char* name, const ImVec2& size = ImVec2(-1.0f, -1.0f)); + OAPIFUNC void EndGroupPanel(); + OAPIFUNC bool MenuButton(const char *label, const char *tooltip = NULL, float xoffset = 0.0f); + OAPIFUNC void PushFont(ImGuiFont); +}; diff --git a/Sound/XRSound/LICENSE b/Sound/XRSound/LICENSE index 2fc172fd5..bc512b7d0 100644 --- a/Sound/XRSound/LICENSE +++ b/Sound/XRSound/LICENSE @@ -30,7 +30,7 @@ about the ikkLang license. Its license as of 31-July-2021 is quoted below: The irrKlang License irrKlang's source codes, documentation and binaries contained within the -distributed archive are copyright © Nikolaus Gebhardt / Ambiera 2001-2020. +distributed archive are copyright � Nikolaus Gebhardt / Ambiera 2001-2020. The contents of the irrKlang distribution archive may not be redistributed, reproduced, modified, transmitted, broadcast, published or adapted in any way, diff --git a/Sound/XRSound/src/CMakeLists.txt b/Sound/XRSound/src/CMakeLists.txt index 497c2ecac..8ad26fc3f 100644 --- a/Sound/XRSound/src/CMakeLists.txt +++ b/Sound/XRSound/src/CMakeLists.txt @@ -23,12 +23,14 @@ target_include_directories(XRSound_dll PUBLIC ${ORBITER_SOURCE_SDK_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Utils ${IRRKLANG_DIR}/include + SDL3::SDL3 ) -target_link_libraries(XRSound_dll +target_link_libraries(XRSound_dll PUBLIC ${ORBITER_LIB} ${ORBITER_SDK_LIB} ${IRRKLANG_LIB} + SDL3::SDL3 ) add_dependencies(XRSound_dll @@ -54,8 +56,11 @@ set_target_properties(XRSound_lib target_include_directories(XRSound_lib PUBLIC ${ORBITER_SOURCE_SDK_INCLUDE_DIR} + SDL3::SDL3 ) +target_link_libraries(XRSound_lib PUBLIC SDL3::SDL3) + install(TARGETS XRSound_lib ARCHIVE DESTINATION ${ORBITER_INSTALL_SDK_DIR}/XRSound ) diff --git a/Src/Celbody/Vsop87/CMakeLists.txt b/Src/Celbody/Vsop87/CMakeLists.txt index c66fb74fe..72f394a67 100644 --- a/Src/Celbody/Vsop87/CMakeLists.txt +++ b/Src/Celbody/Vsop87/CMakeLists.txt @@ -14,11 +14,11 @@ add_dependencies(Vsop87 ) target_include_directories(Vsop87 - PUBLIC ${CMAKE_SOURCE_DIR}/Orbitersdk/include + PUBLIC ${CMAKE_SOURCE_DIR}/Orbitersdk/include SDL3::SDL3 ) target_link_libraries(Vsop87 - ${ORBITER_LIB} + ${ORBITER_LIB} SDL3::SDL3 ) set(VSOP87_LIB $) diff --git a/Src/Module/LuaScript/LuaInline/LuaInline.cpp b/Src/Module/LuaScript/LuaInline/LuaInline.cpp index 4f3e6ee67..f30eac185 100644 --- a/Src/Module/LuaScript/LuaInline/LuaInline.cpp +++ b/Src/Module/LuaScript/LuaInline/LuaInline.cpp @@ -29,7 +29,6 @@ // ============================================================== // class InterpreterList::Environment: implementation -static NOTEHANDLE errorbox; InterpreterList::Environment::Environment() { @@ -37,7 +36,6 @@ InterpreterList::Environment::Environment() singleCmd = false; hThread = NULL; interp = CreateInterpreter (); - interp->SetErrorBox(errorbox); } InterpreterList::Environment::~Environment() @@ -108,17 +106,11 @@ InterpreterList::~InterpreterList () void InterpreterList::clbkSimulationStart (RenderMode mode) { - errorbox = ::oapiCreateAnnotation(false, 1, _V(1.0,0,0)); - ::oapiAnnotationSetPos (errorbox, 0, 0.75, 1, 1); - - for (int i = 0; i < nlist; i++) // prune all finished interpreters - list[i]->interp->SetErrorBox(errorbox); } void InterpreterList::clbkSimulationEnd () { while (nlist) DelInterpreter(list[0]); - oapiDelAnnotation(errorbox); } void InterpreterList::clbkPostStep (double simt, double simdt, double mjd) diff --git a/Src/Module/LuaScript/LuaInterpreter/Interpreter.cpp b/Src/Module/LuaScript/LuaInterpreter/Interpreter.cpp index eadc56ec1..b8d17b470 100644 --- a/Src/Module/LuaScript/LuaInterpreter/Interpreter.cpp +++ b/Src/Module/LuaScript/LuaInterpreter/Interpreter.cpp @@ -96,7 +96,7 @@ int Interpreter::LuaCall(lua_State *L, int narg, int nres) lua_remove(L, base); if(res != 0) { oapiWriteLogError("%s", lua_tostring(L, -1)); - oapiAnnotationSetText(errorbox, const_cast(lua_tostring(L, -1))); + oapiAddNotification(OAPINOTIF_ERROR, "Lua error", lua_tostring(L, -1)); } return res; } @@ -806,6 +806,7 @@ void Interpreter::LoadAPI () {"open_inputbox", oapiOpenInputBox}, {"receive_input", oapiReceiveInput}, {"open_inputboxex", oapi_open_inputboxex}, + {"add_notification", oapi_add_notification}, {"del_vessel", oapi_del_vessel}, {"create_vessel", oapi_create_vessel}, {"set_focusobject", oapi_set_focusobject}, @@ -1344,6 +1345,13 @@ void Interpreter::LoadAPI () lua_createtable (L, 0, 1); lua_pushnumber (L, MESHPROPERTY_MODULATEMATALPHA); lua_setfield (L, -2, "MODULATEMATALPHA"); lua_setglobal (L, "MESHPROPERTY"); + + lua_createtable (L, 0, 4); + lua_pushnumber (L, OAPINOTIF_SUCCESS); lua_setfield (L, -2, "SUCCESS"); + lua_pushnumber (L, OAPINOTIF_WARNING); lua_setfield (L, -2, "WARNING"); + lua_pushnumber (L, OAPINOTIF_ERROR); lua_setfield (L, -2, "ERROR"); + lua_pushnumber (L, OAPINOTIF_INFO); lua_setfield (L, -2, "INFO"); + lua_setglobal (L, "OAPINOTIF"); } void Interpreter::LoadMFDAPI () @@ -4019,6 +4027,28 @@ int Interpreter::oapi_open_inputboxex (lua_State *L) return 0; } +/*** +Display a notification on the screen + +Error notifications are persistant and must be acknowledged + +@function add_notification +@tparam number type Notification type (see table OAPINOTIF) +@tparam string title Notification title +@tparam[opt=""] string content Notification content +*/ +int Interpreter::oapi_add_notification (lua_State *L) +{ + int type = luaL_checkinteger(L, 1); + const char *title = luaL_checkstring(L, 2); + const char *content = ""; + if(lua_gettop (L) >= 3) { + content = luaL_checkstring(L, 3); + } + oapiAddNotification(type, title, content); + return 0; +} + /*** Return the equatorial coordinates with respect to an object of a point given in the global reference frame. diff --git a/Src/Module/LuaScript/LuaInterpreter/Interpreter.h b/Src/Module/LuaScript/LuaInterpreter/Interpreter.h index d9c199db7..f780f4417 100644 --- a/Src/Module/LuaScript/LuaInterpreter/Interpreter.h +++ b/Src/Module/LuaScript/LuaInterpreter/Interpreter.h @@ -234,10 +234,8 @@ class INTERPRETERLIB Interpreter { void term_setverbosity (int level) { term_verbose = level; } static int LuaCall(lua_State *L, int nargs, int nres); - void SetErrorBox(NOTEHANDLE eb) { errorbox = eb; } static void DeleteVessel (OBJHANDLE hVessel); protected: - static inline NOTEHANDLE errorbox; lua_State *L; // Lua main context /** @@ -403,6 +401,7 @@ class INTERPRETERLIB Interpreter { static int oapiOpenInputBox (lua_State *L); static int oapiReceiveInput (lua_State *L); static int oapi_open_inputboxex (lua_State *L); + static int oapi_add_notification (lua_State *L); static int oapi_global_to_equ(lua_State* L); static int oapi_global_to_local(lua_State* L); static int oapi_local_to_equ(lua_State* L); diff --git a/Src/Module/LuaScript/LuaInterpreter/types.lua b/Src/Module/LuaScript/LuaInterpreter/types.lua index 317332e5e..cf3745246 100644 --- a/Src/Module/LuaScript/LuaInterpreter/types.lua +++ b/Src/Module/LuaScript/LuaInterpreter/types.lua @@ -295,6 +295,13 @@ -- @field NOISE Enable/Setup Noise generation -- @table PRM +--- Notification types +-- @field NOTIF_SUCCESS +-- @field NOTIF_WARNING +-- @field NOTIF_ERROR +-- @field NOTIF_INFO +-- @table OAPINOTIF + --- PlaybackType. -- -- Determines how a given sound will be played back (i.e., where it will be audible) diff --git a/Src/Orbiter/Astro.cpp b/Src/Orbiter/Astro.cpp index f236a05d6..13eb3cd9a 100644 --- a/Src/Orbiter/Astro.cpp +++ b/Src/Orbiter/Astro.cpp @@ -208,7 +208,7 @@ char *SciStr (double f, int precision, char prefix) for (i = 0; i < len; i++) { if (strbuf[i] == 'e') { sscanf(strbuf+i+1, "%d", &e); - sprintf (strbuf+i, "·10^%d", e); + sprintf (strbuf+i, u8"·10^%d", e); } } return strbuf; diff --git a/Src/Orbiter/CMakeLists.txt b/Src/Orbiter/CMakeLists.txt index 597b1e4e5..fe39f3ca2 100644 --- a/Src/Orbiter/CMakeLists.txt +++ b/Src/Orbiter/CMakeLists.txt @@ -3,6 +3,7 @@ # SAFESEH linker flag must be turned off because the DX7 libraries don't support it' set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -SAFESEH:NO") +FetchContent_MakeAvailable(imgui) # Sources for all Orbiter executable targets set(common_src @@ -109,6 +110,7 @@ set(common_src D3d7util.cpp D3dmath.cpp Di7frame.cpp + SDLWrappers.cpp # Graphics interface base class for GDI clients ${GDICLIENT_DIR}/GDIClient.cpp # Utils @@ -119,6 +121,8 @@ set(common_src # Resources Orbiter.rc Orbiter.ico + ${imgui_SOURCE_DIR}/backends/imgui_impl_win32.cpp + ${imgui_SOURCE_DIR}/backends/imgui_impl_sdl3.cpp ) set(Orbiter_includes @@ -139,6 +143,7 @@ set(Orbiter_common_libs ${HTMLHELP_LIB} $ $ + SDL3::SDL3 ) set(Orbiter_libs dxguid.lib @@ -163,7 +168,7 @@ set_target_properties(Orbiter FOLDER Core ) -target_include_directories(Orbiter PUBLIC ${Orbiter_includes}) +target_include_directories(Orbiter PUBLIC ${Orbiter_includes} ${imgui_SOURCE_DIR}/ ${imgui_SOURCE_DIR}/backends) target_link_libraries(Orbiter ${Orbiter_common_libs} ${Orbiter_libs}) diff --git a/Src/Orbiter/Camera.cpp b/Src/Orbiter/Camera.cpp index 207970b21..f80343d09 100644 --- a/Src/Orbiter/Camera.cpp +++ b/Src/Orbiter/Camera.cpp @@ -69,7 +69,7 @@ Camera::Camera (double _nearplane, double _farplane) has_tref = false; movehead = false; ExtCtrlMode = 0; - GetCursorPos (&pm); + SDL_GetMouseState (&pm.x, &pm.y); mmoveT = -1000.0; ap_int = ap_ext = RAD*25.0; ap = &ap_ext; @@ -90,51 +90,51 @@ Camera::~Camera () ClearPresets(); } -bool Camera::ProcessMouse (UINT event, DWORD state, DWORD x, DWORD y, const char *kstate) +bool Camera::ProcessMouse (const SDL_Event &event, DWORD x, DWORD y, const char *kstate) { - if (event != WM_MOUSEWHEEL) + if (event.type != SDL_EVENT_MOUSE_WHEEL) mx = (int)x, my = (int)y; - switch (event) { - case WM_LBUTTONDOWN: - mbdown[0] = true; - return true; - case WM_RBUTTONDOWN: - mbdown[1] = true; - g_pOrbiter->InitRotationMode(); - return true; - case WM_LBUTTONUP: - if (mbdown[0]) { - mbdown[0] = false; - return true; - } - break; - case WM_RBUTTONUP: - if (mbdown[1]) { - mbdown[1] = false; - g_pOrbiter->ExitRotationMode(); - return true; - } - break; - case WM_MOUSEWHEEL: - if (KEYMOD_CONTROL(kstate)) { - } - else { - short zDelta = (short)HIWORD(state); - if (external_view) - ShiftDist(-zDelta*0.001); - else - g_pOrbiter->IncFOV(zDelta*(-2.0 / 120.0*RAD)); - } return true; - break; - } + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { + mbdown[0] = true; + return true; + } + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_RIGHT) { + mbdown[1] = true; + g_pOrbiter->InitRotationMode(); + return true; + } + if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) { + if (mbdown[0]) { + mbdown[0] = false; + return true; + } + } + if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_RIGHT) { + if (mbdown[1]) { + mbdown[1] = false; + g_pOrbiter->ExitRotationMode(); + return true; + } + } + if (event.type == SDL_EVENT_MOUSE_WHEEL) { + if (!KEYMOD_CONTROL(kstate)) { + double zDelta = event.wheel.y * 120.0; + if (external_view) + ShiftDist(-zDelta*0.001); + else + g_pOrbiter->IncFOV(zDelta*(-2.0 / 120.0*RAD)); + return true; + } + } + return false; } void Camera::UpdateMouse () { - POINT pt; - GetCursorPos (&pt); + SDL_FPoint pt; + SDL_GetMouseState(&pt.x, &pt.y); if (pt.x != pm.x || pt.y != pm.y) { pm.x = pt.x, pm.y = pt.y; mmoveT = td.SysT0; @@ -143,11 +143,9 @@ void Camera::UpdateMouse () if (mbdown[1]) { int dx, dy, x0, y0; x0 = pt.x, y0 = pt.y; - if (!g_pOrbiter->IsFullscreen()) - ScreenToClient (g_pOrbiter->GetRenderWnd(), &pt); dx = pt.x - mx; dy = pt.y - my; - SetCursorPos (x0-dx, y0-dy); + SDL_WarpMouseInWindow(nullptr, x0-dx, y0-dy); if (!(dx || dy)) return; if (external_view) { @@ -808,7 +806,7 @@ void Camera::SetGroundObserver_TargetLock (bool lock) go.phi = atan2 (-hdir.x, hdir.z); OutputGroundObserverParams(); } - + SendDlgMessage (2, this); } } @@ -829,7 +827,7 @@ void Camera::GroundObserverShift (double dx, double dz, double dh) { if (!external_view || extmode != CAMERA_GROUNDOBSERVER) return; double r; - + Vector dsz (grot.m13, grot.m23, grot.m33); // dz: go forward/backward w.r.t. camera view direction Vector dsx (grot.m11, grot.m21, grot.m31); // dx: go sideways w.r.t. camera view direction dirref->GlobalToEquatorial (gpos + dsz*dz + dsx*dx, go.lng, go.lat, r); @@ -868,7 +866,7 @@ void Camera::OutputGroundObserverParams () const HWND dlg = dlgmgr->IsEntry (g_pOrbiter->GetInstance(), IDD_CAMERA); if (dlg) { char cbuf[256]; - sprintf (cbuf, "Lng = %+0.6f°\r\nLat = %+0.6f°\r\nAlt = %0.2fm\r\nPhi = %0.2f°\r\nTheta = %0.2f°", + sprintf (cbuf, "Lng = %+0.6f°\r\nLat = %+0.6f°\r\nAlt = %0.2fm\r\nPhi = %0.2f°\r\nTheta = %0.2f°", DEG*go.lng, DEG*go.lat, go.alt, DEG*go.phi, DEG*go.tht); SendDlgMessage (1, cbuf); } diff --git a/Src/Orbiter/Camera.h b/Src/Orbiter/Camera.h index 5292a7d64..df3b0d716 100644 --- a/Src/Orbiter/Camera.h +++ b/Src/Orbiter/Camera.h @@ -15,11 +15,15 @@ #ifndef __CAMERA_H #define __CAMERA_H -#include -#include +#include "CamAPI.h" #include "Vecmat.h" #include "elevmgr.h" -#include "CamAPI.h" +#include +#include +#include +#include +#include +namespace fs = std::filesystem; class Body; class Planet; @@ -236,7 +240,7 @@ class Camera { void SetGroundObserver_TerrainLimit (double alt); void OutputGroundObserverParams () const; - bool ProcessMouse (UINT event, DWORD state, DWORD x, DWORD y, const char *kstate); + bool ProcessMouse (const SDL_Event &event, DWORD x, DWORD y, const char *kstate); void UpdateMouse (); void ClearPresets (); @@ -320,7 +324,7 @@ class Camera { DWORD ExtCtrlMode; // if camera is externally controlled, this contains bitflags for data types // (see CAMDATA_xxx constants in CamAPI.h) - POINT pm; // last cursor position + SDL_FPoint pm; // last cursor position double mmoveT; // time of last mouse move Vector gpos; // current camera pos in global coords diff --git a/Src/Orbiter/Config.cpp b/Src/Orbiter/Config.cpp index e0f243ac2..8330c148b 100644 --- a/Src/Orbiter/Config.cpp +++ b/Src/Orbiter/Config.cpp @@ -194,9 +194,9 @@ CFG_DEVPRM CfgDevPrm_default = { CFG_JOYSTICKPRM CfgJoystickPrm_default = { 0, // Joy_idx (joystick device index, 0=disabled) - 2500, // Deadzone (neutralise joystick axes within 20% of central position) + 6500, // Deadzone (neutralise joystick axes within 20% of central position) 1, // ThrottleAxis (z-axis by default) - 9500, // ThrottleSaturation (saturate throttle at the last 5% each end) + 31100, // ThrottleSaturation (saturate throttle at the last 5% each end) true // bThrottleIgnore (ignore throttle setting on simulation start) }; @@ -227,7 +227,9 @@ CFG_DEMOPRM CfgDemoPrm_default = { CFG_FONTPRM CfgFontPrm_default = { 1.0f, // dlgFont_Scale (scaling factor for inline dialog fonts) - "Arial" // dlgFont1_Face (default dialog font face name) + "Arial", // dlgFont1_Face (default dialog font face name) + 14.0f, // ImGui_FontSize + "Roboto-Medium.ttf" // ImGui_FontFile }; CFG_CAMERAPRM CfgCameraPrm_default = { @@ -459,6 +461,10 @@ Config::Config(char* fname) Load(fname); } +static bool operator==(const SDL_GUID a, const SDL_GUID b) { + return memcmp(&a.data, &b.data, 16) == 0; +} + bool Config::Load(const char *fname) { int i; @@ -529,14 +535,25 @@ bool Config::Load(const char *fname) if (GetInt (ifs, "WindowHeight", i)) CfgDevPrm.WinH = (DWORD)i; // Joystick information - if (GetInt (ifs, "JoystickIndex", i)) - CfgJoystickPrm.Joy_idx = (DWORD)i; + if (GetString (ifs, "JoystickIndex", cbuf)) { + const auto guid = SDL_StringToGUID(cbuf); + auto njoy = 0; + const auto joylist = SDL_GetJoysticks(&njoy); + CfgJoystickPrm.Joy_idx = 0; + for (int joyix = 0; joyix < njoy; joyix++) { + if (SDL_GetJoystickGUIDForID(joylist[joyix]) == guid) { + CfgJoystickPrm.Joy_idx = joylist[joyix]; + break; + } + } + SDL_free(joylist); + } if (GetInt (ifs, "JoystickThrottleAxis", i)) CfgJoystickPrm.ThrottleAxis = max (0, min (3, i)); if (GetInt (ifs, "JoystickThrottleSaturation", i)) - CfgJoystickPrm.ThrottleSaturation = max (0, min (10000, i)); + CfgJoystickPrm.ThrottleSaturation = max (0, min (32768, i)); if (GetInt (ifs, "JoystickDeadzone", i)) - CfgJoystickPrm.Deadzone = max (0, min (10000, i)); + CfgJoystickPrm.Deadzone = max (0, min (32768, i)); GetBool (ifs, "IgnoreThrottleOnStart", CfgJoystickPrm.bThrottleIgnore); // planet render parameters @@ -719,14 +736,14 @@ bool Config::Load(const char *fname) GetBool (ifs, "ShowWarpAlways", CfgUIPrm.bWarpAlways); GetBool (ifs, "ShowWarpScientific", CfgUIPrm.bWarpScientific); if (GetInt (ifs, "InfobarMode", i) && i >= 0 && i <= 2) - CfgUIPrm.InfoMode = (DWORD)i; + CfgUIPrm.InfoMode = i; if (GetString (ifs, "InfoAuxIdx", cbuf)) { sscanf (cbuf, "%d%d", CfgUIPrm.InfoAuxIdx+0, CfgUIPrm.InfoAuxIdx+1); for (i = 0; i < 2; i++) if (CfgUIPrm.InfoAuxIdx[i] > 3) CfgUIPrm.InfoAuxIdx[i] = 0; } if (GetInt (ifs, "MenubarOpacity", i) && i >= 0 && i <= 10) - CfgUIPrm.MenuOpacity = (DWORD)i; + CfgUIPrm.MenuOpacity = i; if (GetInt (ifs, "InfobarOpacity", i) && i >= 0 && i <= 10) CfgUIPrm.InfoOpacity = (DWORD)i; if (GetInt (ifs, "MenubarSpeed", i) && i >= 1 && i <= 20) @@ -758,6 +775,8 @@ bool Config::Load(const char *fname) // font characteristics if (GetReal (ifs, "DialogFont_Scale", d)) CfgFontPrm.dlgFont_Scale = (float)d; GetString (ifs, "DialogFont1_Face", CfgFontPrm.dlgFont1_Face); + if (GetReal (ifs, "ImGui_FontSize", d)) CfgFontPrm.ImGui_FontSize = (float)d; + GetString (ifs, "ImGui_FontFile", CfgFontPrm.ImGui_FontFile); // misc. options if (GetString (ifs, "LPadRect", cbuf)) { @@ -1246,8 +1265,12 @@ BOOL Config::Write (const char *fname) const if (memcmp (&CfgJoystickPrm, &CfgJoystickPrm_default, sizeof(CFG_JOYSTICKPRM)) || bEchoAll) { ofs << "\n; === Joystick parameters ===\n"; - if (CfgJoystickPrm.Joy_idx != CfgJoystickPrm_default.Joy_idx || bEchoAll) - ofs << "JoystickIndex = " << CfgJoystickPrm.Joy_idx << '\n'; + if (CfgJoystickPrm.Joy_idx != CfgJoystickPrm_default.Joy_idx || bEchoAll) { + const auto guid = SDL_GetJoystickGUIDForID(CfgJoystickPrm.Joy_idx); + char cbuf[33]; + SDL_GUIDToString(guid, cbuf, 33); + ofs << "JoystickIndex = " << cbuf << '\n'; + } if (CfgJoystickPrm.ThrottleAxis != CfgJoystickPrm_default.ThrottleAxis || bEchoAll) ofs << "JoystickThrottleAxis = " << CfgJoystickPrm.ThrottleAxis << '\n'; if (CfgJoystickPrm.ThrottleSaturation != CfgJoystickPrm_default.ThrottleSaturation || bEchoAll) @@ -1332,6 +1355,10 @@ BOOL Config::Write (const char *fname) const ofs << "DialogFont_Scale = " << CfgFontPrm.dlgFont_Scale << '\n'; if (strcmp (CfgFontPrm.dlgFont1_Face, CfgFontPrm_default.dlgFont1_Face) || bEchoAll) ofs << "DialogFont1_Face = " << CfgFontPrm.dlgFont1_Face << '\n'; + if (CfgFontPrm.ImGui_FontSize != CfgFontPrm_default.ImGui_FontSize || bEchoAll) + ofs << "ImGui_FontSize = " << CfgFontPrm.ImGui_FontSize << '\n'; + if (strcmp (CfgFontPrm.ImGui_FontFile, CfgFontPrm_default.ImGui_FontFile) || bEchoAll) + ofs << "ImGui_FontFile = " << CfgFontPrm.ImGui_FontFile << '\n'; } if (memcmp (&CfgWindowPos, &CfgWindowPos_default, sizeof(CFG_WINDOWPOS)) || bEchoAll) { diff --git a/Src/Orbiter/Config.h b/Src/Orbiter/Config.h index c41db2263..02407183e 100644 --- a/Src/Orbiter/Config.h +++ b/Src/Orbiter/Config.h @@ -11,12 +11,16 @@ #define __CONFIG_H //#include -#include +#include "GraphicsAPI.h" #include "Vecmat.h" -#include #include +#include #include -#include "GraphicsAPI.h" +#include +#include + +#include +namespace fs = std::filesystem; // dynamic state propagation methods #define MAX_PROP_LEVEL 5 @@ -218,25 +222,25 @@ struct CFG_DEVPRM { }; struct CFG_JOYSTICKPRM { - DWORD Joy_idx; // joystick device index (0=disabled) - DWORD Deadzone; // central deadzone range for all axes (0-10000) - DWORD ThrottleAxis; // joystick throttle axis (0=none, 1=z-axis, 2=slider 0, 3=slider 1) - DWORD ThrottleSaturation; // saturation level for joystick throttle control (0-10000) - bool bThrottleIgnore; // ignore joystick throttle setting on start + SDL_JoystickID Joy_idx; // joystick device index (0=disabled) + DWORD Deadzone; // central deadzone range for all axes (0-32768) + DWORD ThrottleAxis; // joystick throttle axis (0=none, 1=z-axis, 2=slider 0, 3=slider 1) + DWORD ThrottleSaturation; // saturation level for joystick throttle control (0-32768) + bool bThrottleIgnore; // ignore joystick throttle setting on start }; struct CFG_UIPRM { // user interface options DWORD MouseFocusMode; // 0: focus requires click; 1: focus requires click for child windows only; 2: focus follow mouse - DWORD MenuMode; // 0=show, 1=hide, 2=auto-hide + int MenuMode; // 0=show, 1=hide, 2=auto-hide bool bMenuLabelOnly; // display only menu labels? bool bWarpAlways; // always display time acceleration != 1 bool bWarpScientific; // display time acceleration in scientific notation? - DWORD InfoMode; // 0=show, 1=hide, 2=auto-hide - DWORD InfoAuxIdx[2]; // index for auxiliary info bars left/right (0=none) - DWORD MenuOpacity; // menubar opacity (0-10) - DWORD InfoOpacity; // infobar opacity (0-20) - DWORD MenuScrollspeed; // menubar scroll speed (1-20) - DWORD PauseIndMode; // 0=flash on pause/resume, 1=show on pause, 2=don't show + int InfoMode; // 0=show, 1=hide, 2=auto-hide + int InfoAuxIdx[2]; // index for auxiliary info bars left/right (0=none) + int MenuOpacity; // menubar opacity (0-10) + int InfoOpacity; // infobar opacity (0-20) + int MenuScrollspeed; // menubar scroll speed (1-20) + int PauseIndMode; // 0=flash on pause/resume, 1=show on pause, 2=don't show int SelVesselTab; // tab to open in vessel selection dialog int SelVesselRange; // "nearby" range for vessel selection dialog bool bSelVesselFlat; // flat assemblies for vessel selection dialog @@ -253,6 +257,8 @@ struct CFG_DEMOPRM { struct CFG_FONTPRM { float dlgFont_Scale; // font scaling factor char dlgFont1_Face[64]; // dialog font face name + float ImGui_FontSize; // Font size for ImGui dialogs + char ImGui_FontFile[256]; // Font file for ImGui default font }; struct CFG_CAMERAPRM { @@ -309,7 +315,7 @@ bool GetItemVector (std::istream &is, const char *label, Vector &val); bool GetItemVECTOR (std::istream &is, const char *label, VECTOR3 &val); bool FindLine (std::istream &is, const char *line); -// scans stream 'is' from beginning for a line beginning with 'line' +// scans stream 'is' from beginning for a line beginning with 'line' // and leaves file pointer on the beginning of the next line // return value is false if line is not found @@ -484,4 +490,4 @@ GDIResources *g_gdires = 0; extern GDIResources *g_gdires; #endif -#endif // !__CONFIG_H \ No newline at end of file +#endif // !__CONFIG_H diff --git a/Src/Orbiter/Defpanel.cpp b/Src/Orbiter/Defpanel.cpp index 7e0327999..7e331f381 100644 --- a/Src/Orbiter/Defpanel.cpp +++ b/Src/Orbiter/Defpanel.cpp @@ -539,25 +539,20 @@ void DefaultPanel::Render () gc->clbkRender2DPanel (&surf, (MESHHANDLE)&mesh, &transf, transpmfd); } -bool DefaultPanel::ProcessMouse (UINT event, DWORD state, int x, int y) +bool DefaultPanel::ProcessMouse (const SDL_Event &event, int x, int y) { - switch (event) { - case WM_LBUTTONDOWN: - mstate = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; - break; - case WM_RBUTTONDOWN: - mstate = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; - break; - case WM_LBUTTONUP: - mstate = PANEL_MOUSE_LBUP; - break; - case WM_RBUTTONUP: - mstate = PANEL_MOUSE_RBUP; - break; - default: - mstate = 0; - break; - } + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { + mstate = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_RIGHT) { + mstate = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) { + mstate = PANEL_MOUSE_LBUP; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_RIGHT) { + mstate = PANEL_MOUSE_RBUP; + } else { + mstate = 0; + } + if (mstate & PANEL_MOUSE_DOWN) { // locate mouse event int mfd, btn; if (GetMFDButton (x, y, mfd, btn)) { diff --git a/Src/Orbiter/Defpanel.h b/Src/Orbiter/Defpanel.h index f50d53cce..465584840 100644 --- a/Src/Orbiter/Defpanel.h +++ b/Src/Orbiter/Defpanel.h @@ -30,7 +30,7 @@ class DefaultPanel { // Render the glass cockpit overlay on top of the target surface void Render (); - bool ProcessMouse (UINT event, DWORD state, int x, int y); + bool ProcessMouse (const SDL_Event &event, int x, int y); void GetButtonState (int &state, int &mfd, int &btn); inline void SetMouseState (int state) { mstate = state; } inline void SetNavDisplayMode (int mode) { navdispmode = mode; } diff --git a/Src/Orbiter/Dialogs.cpp b/Src/Orbiter/Dialogs.cpp index 35b94e9b1..dfad4e9cb 100644 --- a/Src/Orbiter/Dialogs.cpp +++ b/Src/Orbiter/Dialogs.cpp @@ -109,27 +109,6 @@ INT_PTR CALLBACK Navaid_DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPa } #endif -// ====================================================================== -// "Recorder/player" dialog -// ====================================================================== - -INT_PTR CALLBACK FRecorderMsg_DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - g_pOrbiter->ToggleRecorder (true); - // fall through - case IDCANCEL: - g_pOrbiter->CloseDialog (hDlg); - break; - } - break; - } - return OrbiterDefDialogProc (hDlg, uMsg, wParam, lParam); -} - // ========================================================================= // Set up a combo box dialog control for selecting a celestial body diff --git a/Src/Orbiter/Dialogs.h b/Src/Orbiter/Dialogs.h index d0ec2b948..fe3e660a9 100644 --- a/Src/Orbiter/Dialogs.h +++ b/Src/Orbiter/Dialogs.h @@ -14,10 +14,6 @@ #include "DlgCapture.h" #include "DlgOptions.h" -//INT_PTR CALLBACK Navaid_DlgProc (HWND, UINT, WPARAM, LPARAM); - -INT_PTR CALLBACK FRecorderMsg_DlgProc (HWND, UINT, WPARAM, LPARAM); - // ========================================================================= // Set up a combo box dialog control for selecting a celestial body diff --git a/Src/Orbiter/DlgCamera.cpp b/Src/Orbiter/DlgCamera.cpp index dec3b3d46..8d390af3f 100644 --- a/Src/Orbiter/DlgCamera.cpp +++ b/Src/Orbiter/DlgCamera.cpp @@ -1,1204 +1,430 @@ -// Copyright (c) Martin Schweiger -// Licensed under the MIT License - -// ====================================================================== -// Camera configuration dialog -// ====================================================================== - -#define STRICT 1 - #include "DlgCamera.h" -#include "DlgMgr.h" -#include "Resource.h" -#include "Resource2.h" +#include "OrbiterAPI.h" #include "Orbiter.h" +#include "Celbody.h" #include "Psys.h" -#include "Camera.h" -#include "Uxtheme.h" -#include "DlgCtrl.h" +#include "imgui.h" +#include "imgui_extras.h" +#include "IconsFontAwesome6.h" -using std::min; -using std::max; - -extern Orbiter *g_pOrbiter; extern PlanetarySystem *g_psys; extern Camera *g_camera; -extern Vessel *g_focusobj; extern TimeData td; -extern HELPCONTEXT DefHelpContext; - -// ====================================================================== - -DlgCamera::DlgCamera (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_CAMERA, 0, 0, context) -{ - nTab = 0; - hcontext = 0; - pos = &g_pOrbiter->Cfg()->CfgWindowPos.DlgCamera; -} - -// ====================================================================== - -DlgCamera::~DlgCamera () -{ - Clear (); -} - -// ====================================================================== - -void DlgCamera::Update () -{ - for (int i = 0; i < nTab; i++) - pTab[i]->Update (); -} - -// ====================================================================== - -void DlgCamera::ModeChanged () -{ - pTabControl->ModeChanged (); -} - -// ====================================================================== - -void DlgCamera::FovChanged (double fov) -{ - pTabFov->FovChanged (fov); -} - -// ====================================================================== - -void DlgCamera::GObserverChanged (const char *str) -{ - pTabGround->GObserverChanged (str); -} - -// ====================================================================== - -void DlgCamera::Refresh (const Camera *cam) -{ - pTabGround->LockChanged (cam->GroundObserver_TargetLock()); - //pTabFov->FovChanged (cam->Aperture() * 2.0*DEG); -} - -// ====================================================================== - -BOOL DlgCamera::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - HWND hTabFrame = hDlg; - - AddTab (hDlg, pTabControl = new TabControl (hTabFrame), "Control"); - AddTab (hDlg, new TabTarget (hTabFrame), "Target"); - AddTab (hDlg, new TabView (hTabFrame), "Track"); - AddTab (hDlg, pTabGround = new TabGround (hTabFrame), "Ground"); - AddTab (hDlg, pTabFov = new TabFov (hTabFrame), "FoV"); - AddTab (hDlg, new TabPreset (hTabFrame), "Preset"); - - SwitchTab (hDlg); - return TRUE; -} - -// ====================================================================== - -BOOL DlgCamera::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDHELP: - DefHelpContext.topic = HelpContext (); - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); -} - -// ====================================================================== - -BOOL DlgCamera::OnNotify (HWND hDlg, int idCtrl, LPNMHDR pnmh) -{ - if (pnmh->idFrom == IDC_CAM_TAB) { - if (pnmh->code == TCN_SELCHANGE) SwitchTab (hDlg); - return TRUE; - } - return MSG_DEFAULT; -} - -// ====================================================================== - -BOOL DlgCamera::OnApp (HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - switch (LOWORD (wParam)) { - case 0: // FOV change notification - FovChanged (2.0*DEG * *(double*)lParam); - return TRUE; - case 1: // Ground observer position changed - GObserverChanged ((const char*)lParam); - return TRUE; - case 2: // Ground camera target lock mode changed - Refresh ((const Camera*)lParam); - return TRUE; - case 3: // Camera mode changed - ModeChanged (); - return TRUE; - } - return FALSE; -} - -// ====================================================================== - -void DlgCamera::Clear () -{ - if (nTab) { - for (int i = 0; i < nTab; i++) - delete pTab[i]; - delete []pTab; - pTab = NULL; - nTab = 0; - } -} - -// ====================================================================== - -int DlgCamera::AddTab (HWND hDlg, CameraTab *tab, const char *label) -{ - char cbuf[256]; - strcpy (cbuf, label); - TC_ITEM tie; - tie.mask = TCIF_TEXT; - tie.iImage = -1; - tie.pszText = cbuf; - SendDlgItemMessage (hDlg, IDC_CAM_TAB, TCM_INSERTITEM, nTab, (LPARAM)&tie); - - CameraTab **tmp = new CameraTab*[nTab+1]; - if (nTab) { - memcpy (tmp, pTab, nTab*sizeof(CameraTab*)); - delete []pTab; - } - pTab = tmp; - pTab[nTab] = tab; - return nTab++; -} - -// ====================================================================== - -void DlgCamera::SwitchTab (HWND hDlg) -{ - int pg, cpg = TabCtrl_GetCurSel (GetDlgItem (hDlg, IDC_CAM_TAB)); - for (pg = 0; pg < nTab; pg++) - if (pg != cpg) pTab[pg]->Show (false); - pTab[cpg]->Show (true); - hcontext = pTab[cpg]->HelpContext(); -} - -// ====================================================================== -// ====================================================================== - -CameraTab::CameraTab (HWND hParentTab, int dlgId, DLGPROC dlgProc) -{ - active = false; - hParent = hParentTab; - hTab = CreateDialogParam (g_pOrbiter->GetInstance(), MAKEINTRESOURCE(dlgId), hParentTab, dlgProc, (LPARAM)this); -} - -// ====================================================================== - -CameraTab::~CameraTab () -{ - DestroyWindow (hTab); -} - -// ====================================================================== - -void CameraTab::Show (bool show) -{ - ShowWindow (hTab, show ? SW_SHOW : SW_HIDE); - active = show; -} - -// ====================================================================== - -INT_PTR CALLBACK CameraTab::DlgProcInit (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_INITDIALOG: - EnableThemeDialogTexture (hWnd, ETDT_ENABLETAB); - SetWindowLongPtr (hWnd, DWLP_USER, lParam); - return TRUE; - } - return FALSE; -} - - -// ====================================================================== -// ====================================================================== - -TabControl::TabControl (HWND hParentTab): CameraTab (hParentTab, IDD_CAM_PG_CONTROL, DlgProc) -{ -} - -// ====================================================================== - -char *TabControl::HelpContext () const -{ - return (char*)"/cam_control.htm"; -} - -// ====================================================================== - -BOOL TabControl::Init (HWND hWnd) -{ - HICON hIcon; - HINSTANCE hInst = g_pOrbiter->GetInstance (); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_LARROW)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_ROTL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_RARROW)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_ROTR, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_UARROW)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_ROTU, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_MOVEU, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_DARROW)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_ROTD, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_MOVED, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_UARROW_TILT)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_MOVEIN, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_DARROW_TILT)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_MOVEOUT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_LARROW_TILT)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_MOVEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_RARROW_TILT)); - SendDlgItemMessage (hWnd, IDC_CAM_CTRL_MOVER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); - - GAUGEPARAM gp = { 0, 60, GAUGEPARAM::LEFT, GAUGEPARAM::BLACK }; - oapiSetGaugeParams (GetDlgItem (hWnd, IDC_CAM_CTRL_SENSITIVITY), &gp, true); - int pos = max (0, min (100, (int)((log10 (g_camera->GroundObserver_PanSpeed())+1.0)*10.0))); - oapiSetGaugePos (GetDlgItem (hWnd, IDC_CAM_CTRL_SENSITIVITY), pos); - - return TRUE; -} - -// ====================================================================== - -void TabControl::Show (bool show) -{ - CameraTab::Show (show); - if (show) { - GetCamMode (); - SetLayout (); - } -} - -// ====================================================================== - -void TabControl::Update () -{ - double dt = td.SysDT; +extern Orbiter *g_pOrbiter; +extern Vessel *g_focusobj; - // Get press status of control buttons - if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_ROTL, BM_GETSTATE, 0, 0) & BST_PUSHED) { - if (rot_is_tilt) g_camera->Rotate ( dt, 0); - else g_camera->ShiftPhi (-dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_ROTR, BM_GETSTATE, 0, 0) & BST_PUSHED) { - if (rot_is_tilt) g_camera->Rotate (-dt, 0); - else g_camera->ShiftPhi ( dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_ROTU, BM_GETSTATE, 0, 0) & BST_PUSHED) { - if (rot_is_tilt) g_camera->Rotate ( 0, dt); - else g_camera->ShiftTheta (-dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_ROTD, BM_GETSTATE, 0, 0) & BST_PUSHED) { - if (rot_is_tilt) g_camera->Rotate ( 0, -dt); - else g_camera->ShiftTheta ( dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_MOVEIN, BM_GETSTATE, 0, 0) & BST_PUSHED) { - if (rot_is_pan) g_camera->ShiftTheta (-dt); - else g_camera->ShiftDist (-dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_MOVEOUT, BM_GETSTATE, 0, 0) & BST_PUSHED) { - if (rot_is_pan) g_camera->ShiftTheta ( dt); - else g_camera->ShiftDist (dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_MOVEL, BM_GETSTATE, 0, 0) & BST_PUSHED) { - g_camera->AddPhi (-dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_MOVER, BM_GETSTATE, 0, 0) & BST_PUSHED) { - g_camera->AddPhi ( dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_MOVEU, BM_GETSTATE, 0, 0) & BST_PUSHED) { - g_camera->ChangeDist (1.0+dt); - } - else if (SendDlgItemMessage (hTab, IDC_CAM_CTRL_MOVED, BM_GETSTATE, 0, 0) & BST_PUSHED) { - g_camera->ChangeDist (1.0/(1.0+dt)); - } +DlgCamera::DlgCamera() : ImGuiDialog(ICON_FA_VIDEO " Orbiter: Camera", {512,359}) { + m_SelectedPreset = -1; } -// ====================================================================== - -void TabControl::SetLayout () -{ - const int nCockpitCtrl = 5; - const int CockpitCtrl[nCockpitCtrl] = { - IDC_CAM_CTRL_ROTL, IDC_CAM_CTRL_ROTR, IDC_CAM_CTRL_ROTU, IDC_CAM_CTRL_ROTD, - IDC_CAM_CTRL_FORWARD - }; - const int nTrackCtrl = 6; - const int TrackCtrl[nTrackCtrl] = { - IDC_CAM_CTRL_ROTL, IDC_CAM_CTRL_ROTR, IDC_CAM_CTRL_ROTU, IDC_CAM_CTRL_ROTD, - IDC_CAM_CTRL_MOVEIN, IDC_CAM_CTRL_MOVEOUT - }; - const int nToFromCtrl = 2; - const int ToFromCtrl[nToFromCtrl] = { - IDC_CAM_CTRL_MOVEIN, IDC_CAM_CTRL_MOVEOUT - }; - const int nGroundFreeCtrl = 12; - const int GroundFreeCtrl[nGroundFreeCtrl] = { - IDC_CAM_CTRL_ROTL, IDC_CAM_CTRL_ROTR, IDC_CAM_CTRL_ROTU, IDC_CAM_CTRL_ROTD, - IDC_CAM_CTRL_MOVEIN, IDC_CAM_CTRL_MOVEOUT, IDC_CAM_CTRL_MOVEL, IDC_CAM_CTRL_MOVER, - IDC_CAM_CTRL_MOVEU, IDC_CAM_CTRL_MOVED, - IDC_CAM_CTRL_SENSITIVITY, IDC_CAM_CTRL_SENSITIVITYLABEL - }; - const int nGroundLockCtrl = 8; - const int GroundLockCtrl[nGroundLockCtrl] = { - IDC_CAM_CTRL_ROTL, IDC_CAM_CTRL_ROTR, - IDC_CAM_CTRL_MOVEIN, IDC_CAM_CTRL_MOVEOUT, - IDC_CAM_CTRL_MOVEU, IDC_CAM_CTRL_MOVED, - IDC_CAM_CTRL_SENSITIVITY, IDC_CAM_CTRL_SENSITIVITYLABEL - }; - - int i, nCtrl = 0; - const int *Ctrl = 0; +void DlgCamera::OnDraw() { + extmode = g_camera->GetExtMode (); + intmode = g_camera->GetIntMode (); + extcam = (g_camera->GetMode () != CAM_COCKPIT); + ground_lock = g_camera->GroundObserver_TargetLock(); + rot_is_tilt = (!extcam || (extmode == CAMERA_GROUNDOBSERVER && !ground_lock)); + rot_is_pan = (extcam && extmode == CAMERA_GROUNDOBSERVER); + + if(followterrain[0]=='\0') + sprintf (followterrain, "%0.0lf", g_camera->GroundObserver_TerrainLimit()); + + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; + if (ImGui::BeginTabBar("CameraTabBar", tab_bar_flags)) + { + for(size_t i = 0; i < sizeof(tabs)/sizeof(tabs[0]); i++) { + if(ImGui::BeginTabItem(tabs[i].name)) { + SetHelp("html/orbiter.chm", tabs[i].helptopic); + (this->*tabs[i].func)(); + ImGui::EndTabItem(); + } + } + ImGui::EndTabBar(); + } +} + +void DlgCamera::DrawControl() { + const char *cameraMode = "Internal"; if (extcam) { switch (extmode) { case CAMERA_TARGETRELATIVE: + cameraMode = "Target Relative"; break; case CAMERA_ABSDIRECTION: + cameraMode = "Absolute Direction"; break; case CAMERA_GLOBALFRAME: - nCtrl = nTrackCtrl; - Ctrl = TrackCtrl; - break; + cameraMode = "Global Frame"; break; case CAMERA_TARGETTOOBJECT: + cameraMode = "Target Object"; break; case CAMERA_TARGETFROMOBJECT: - nCtrl = nToFromCtrl; - Ctrl = ToFromCtrl; - break; + cameraMode = "Target from Object"; break; case CAMERA_GROUNDOBSERVER: if (ground_lock) { - nCtrl = nGroundLockCtrl; - Ctrl = GroundLockCtrl; + cameraMode = "Ground Observer (locked)"; break; } else { - nCtrl = nGroundFreeCtrl; - Ctrl = GroundFreeCtrl; - } - break; - } - } else { - nCtrl = nCockpitCtrl; - Ctrl = CockpitCtrl; - } - - for (i = IDC_CAM_CTRL_MIN; i < IDC_CAM_CTRL_MAX; i++) - ShowWindow (GetDlgItem (hTab, i), SW_HIDE); - for (i = 0; i < nCtrl; i++) - ShowWindow (GetDlgItem (hTab, Ctrl[i]), SW_SHOW); - - SetWindowText (GetDlgItem (hTab, IDC_CAM_CTRL_ROTGROUP), rot_is_tilt ? "Tilt" : "Rotate"); -} - -// ====================================================================== - -void TabControl::ModeChanged () -{ - if (active) { - GetCamMode (); - SetLayout (); - } -} - -// ====================================================================== - -void TabControl::GetCamMode () -{ - extmode = g_camera->GetExtMode (); - intmode = g_camera->GetIntMode (); - extcam = (g_camera->GetMode () != CAM_COCKPIT); - ground_lock = g_camera->GroundObserver_TargetLock(); - rot_is_tilt = (!extcam || (extmode == CAMERA_GROUNDOBSERVER && !ground_lock)); - rot_is_pan = (extcam && extmode == CAMERA_GROUNDOBSERVER); -} - -// ====================================================================== - -INT_PTR CALLBACK TabControl::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraTab::DlgProcInit (hWnd, uMsg, wParam, lParam); - TabControl *pTab = (TabControl*)(uMsg == WM_INITDIALOG ? lParam : GetWindowLongPtr(hWnd,DWLP_USER)); - - switch (uMsg) { - case WM_INITDIALOG: - return pTab->Init (hWnd); - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_CAM_CTRL_FORWARD: - g_camera->ResetCockpitDir(); - return TRUE; - } - break; - case WM_HSCROLL: - switch (GetDlgCtrlID ((HWND)lParam)) { - case IDC_CAM_CTRL_SENSITIVITY: - switch (LOWORD(wParam)) { - case SB_THUMBTRACK: - case SB_LINELEFT: - case SB_LINERIGHT: - g_camera->SetGroundObserver_PanSpeed (pow (10, HIWORD(wParam)/10.0-1.0)); - return 0; - } - break; - } - break; - } - return FALSE; -} - - -// ====================================================================== -// ====================================================================== - -TabTarget::TabTarget (HWND hParentTab): CameraTab (hParentTab, IDD_CAM_PG_TARGET, DlgProc) -{ -} - -// ====================================================================== - -char *TabTarget::HelpContext () const -{ - return (char*)"/cam_target.htm"; -} - -// ====================================================================== - -void TabTarget::AddCbodyNode (HWND hWnd, const CelestialBody *cbody, HTREEITEM parent) -{ - char cbuf[256]; cbuf[255] = '\0'; - TV_INSERTSTRUCT tvis; - tvis.hParent = parent; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.pszText = cbuf; - tvis.item.cChildren = (cbody->nSecondary() ? 1:0); - strncpy (cbuf, cbody->Name(), 255); - HTREEITEM hti = (HTREEITEM)SendDlgItemMessage (hWnd, IDC_CAM_TGT_TREE, TVM_INSERTITEM, 0, (LPARAM)&tvis); - - // recursively add children - for (DWORD i = 0; i < cbody->nSecondary(); i++) { - AddCbodyNode (hWnd, cbody->Secondary(i), hti); - } -} - -// ====================================================================== - -void TabTarget::AddVessels (HWND hWnd) -{ - char cbuf[256]; cbuf[255] = '\0'; - HTREEITEM hti; - TV_INSERTSTRUCT tvis; - tvis.hParent = NULL; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.pszText = cbuf; - tvis.item.cChildren = 1; - strcpy (cbuf, "Vessels"); - hti = (HTREEITEM)SendDlgItemMessage (hWnd, IDC_CAM_TGT_TREE, TVM_INSERTITEM, 0, (LPARAM)&tvis); - tvis.hParent = hti; - tvis.item.cChildren = 0; - for (DWORD i = 0; i < g_psys->nVessel(); i++) { - strncpy (cbuf, g_psys->GetVessel(i)->Name(), 255); - SendDlgItemMessage (hWnd, IDC_CAM_TGT_TREE, TVM_INSERTITEM, 0, (LPARAM)&tvis); - } -} - -// ====================================================================== - -void TabTarget::AddSurfbases (HWND hWnd) -{ - int i, j; - char cbuf[256]; cbuf[255] = '\0'; - HTREEITEM hti; - TV_INSERTSTRUCT tvis; - tvis.hParent = NULL; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.pszText = cbuf; - tvis.item.cChildren = 1; - strcpy (cbuf, "Spaceports"); - hti = (HTREEITEM)SendDlgItemMessage (hWnd, IDC_CAM_TGT_TREE, TVM_INSERTITEM, 0, (LPARAM)&tvis); - tvis.hParent = hti; - tvis.item.cChildren = 0; - for (i = 0; i < g_psys->nGrav(); i++) { - Body *obj = g_psys->GetGravObj (i); - if (obj->Type() != OBJTP_PLANET) continue; - Planet *planet = (Planet*)obj; - for (j = 0; j < g_psys->nBase(planet); j++) { - strncpy (cbuf, g_psys->GetBase (planet,j)->Name(), 255); - SendDlgItemMessage (hWnd, IDC_CAM_TGT_TREE, TVM_INSERTITEM, 0, (LPARAM)&tvis); - } - } -} - -// ====================================================================== - -void TabTarget::Show (bool show) -{ - CameraTab::Show (show); - - if (show) - SetWindowText (GetDlgItem (hTab, IDC_CAM_TGT_INPUT), g_camera->Target()->Name()); -} - -// ====================================================================== - -BOOL TabTarget::Init (HWND hWnd) -{ - int i; - - // Fill tree list - for (i = 0; i < g_psys->nStar(); i++) - AddCbodyNode (hWnd, g_psys->GetStar(i), NULL); - AddSurfbases (hWnd); - AddVessels (hWnd); - - return TRUE; -} - -// ====================================================================== - -INT_PTR CALLBACK TabTarget::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraTab::DlgProcInit (hWnd, uMsg, wParam, lParam); - TabTarget *pTab = (TabTarget*)(uMsg == WM_INITDIALOG ? lParam : GetWindowLongPtr(hWnd,DWLP_USER)); - - switch (uMsg) { - case WM_INITDIALOG: - return pTab->Init (hWnd); - case WM_COMMAND: - switch (LOWORD (wParam)) { - case IDC_CAM_TGT_APPLY: { - char cbuf[256]; - GetWindowText (GetDlgItem (hWnd, IDC_CAM_TGT_INPUT), cbuf, 256); - Body *obj = g_psys->GetObj (cbuf, true); - if (!obj) obj = g_psys->GetBase (cbuf, true); - if (obj) { - g_camera->SetDistance(2.0); - g_pOrbiter->SetView(obj, 1); - } - else { - SetWindowText (GetDlgItem (hWnd, IDC_CAM_TGT_INPUT), g_camera->Target()->Name()); - MessageBeep (MB_ICONEXCLAMATION); - } - } return TRUE; - case IDC_CAM_TGT_FCOCKPIT: - if (HIWORD(wParam) == BN_CLICKED) { - g_camera->SetDistance(2.0); - g_pOrbiter->SetView (g_focusobj, 0); - return TRUE; + cameraMode = "Ground Observer"; break; } break; - case IDC_CAM_TGT_FEXTERN: - if (HIWORD(wParam) == BN_CLICKED) { - g_camera->SetDistance(2.0); - g_pOrbiter->SetView (g_focusobj, 1); - return TRUE; - } - break; - } - break; - case WM_NOTIFY: { - NM_TREEVIEW *pnmtv = (NM_TREEVIEW FAR *)lParam; - if (pnmtv->hdr.code == TVN_SELCHANGING) { - HTREEITEM hti = pnmtv->itemNew.hItem; - TV_ITEM tvi; - char cbuf[256]; - tvi.mask = TVIF_HANDLE|TVIF_TEXT; - tvi.hItem = hti; - tvi.pszText = cbuf; - tvi.cchTextMax = 256; - SendDlgItemMessage (hWnd, IDC_CAM_TGT_TREE, TVM_GETITEM, 0, (LPARAM)&tvi); - SetWindowText (GetDlgItem (hWnd, IDC_CAM_TGT_INPUT), cbuf); } - } break; - } - return FALSE; -} - - -// ====================================================================== -// ====================================================================== - -TabView::TabView (HWND hParentTab): CameraTab (hParentTab, IDD_CAM_PG_VIEW, DlgProc) -{ -} - -// ====================================================================== - -char *TabView::HelpContext () const -{ - return (char*)"/cam_track.htm"; -} - -// ====================================================================== - -BOOL TabView::Init (HWND hWnd) -{ - int i, j; - DWORD k; - - // Fill camera reference combo box - for (i = 0; i < g_psys->nStar(); i++) - SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_ADDSTRING, 0, (LPARAM)g_psys->GetStar(i)->Name()); - for (i = 0; i < g_psys->nPlanet(); i++) { - Planet *planet = g_psys->GetPlanet(i); - if (planet->isMoon()) continue; - SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_ADDSTRING, 0, (LPARAM)planet->Name()); - for (j = 0; j < planet->nSecondary(); j++) - SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_ADDSTRING, 0, (LPARAM)planet->Secondary(j)->Name()); - } - for (k = 0; k < g_psys->nVessel(); k++) - SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_ADDSTRING, 0, (LPARAM)g_psys->GetVessel(k)->Name()); - for (k = 0; k < g_psys->nGrav(); k++) { - if (g_psys->GetGravObj(k)->Type() != OBJTP_PLANET) continue; - Planet *planet = (Planet*)g_psys->GetGravObj(k); - for (j = 0; j < g_psys->nBase(planet); j++) - SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_ADDSTRING, 0, (LPARAM)g_psys->GetBase(planet,j)->Name()); } - // remove camera target from list - //i = SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_FINDSTRINGEXACT, 0, (LPARAM)g_camera->Target()->Name()); - //if (i != CB_ERR) SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_DELETESTRING, i, 0); - - // select first item, usually the central star - SendDlgItemMessage (hWnd, IDC_CAM_VIEW_REFLIST, CB_SETCURSEL, 0, 0); - return TRUE; + ImGui::Text("Camera mode : %s", cameraMode); - return TRUE; -} + ImVec2 pos; + double dt = td.SysDT; -// ====================================================================== + pos.x = 50; + pos.y = 75; + ImGui::SetCursorPos(pos); + ImGui::ArrowButton("##up", ImGuiDir_Up); + if(ImGui::IsItemActive()){ + if (rot_is_tilt) g_camera->Rotate ( 0, dt); + else g_camera->ShiftTheta (-dt); + } -INT_PTR CALLBACK TabView::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraTab::DlgProcInit (hWnd, uMsg, wParam, lParam); - TabView *pTab = (TabView*)(uMsg == WM_INITDIALOG ? lParam : GetWindowLongPtr(hWnd,DWLP_USER)); + pos.x = 50; + pos.y = 125; + ImGui::SetCursorPos(pos); + ImGui::ArrowButton("##down", ImGuiDir_Down); + if(ImGui::IsItemActive()){ + if (rot_is_tilt) g_camera->Rotate ( 0, -dt); + else g_camera->ShiftTheta ( dt); + } + + pos.x = 25; + pos.y = 100; + ImGui::SetCursorPos(pos); + ImGui::ArrowButton("##left", ImGuiDir_Left); + if(ImGui::IsItemActive()){ + if (rot_is_tilt) g_camera->Rotate ( dt, 0); + else g_camera->ShiftPhi (-dt); + } - switch (uMsg) { - case WM_INITDIALOG: - pTab->Init(hWnd); - return FALSE; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_CAM_VIEW_TGTREL: - g_camera->SetTrackMode (CAMERA_TARGETRELATIVE); - return FALSE; - case IDC_CAM_VIEW_ABSDIR: - g_camera->SetTrackMode (CAMERA_ABSDIRECTION); - return FALSE; - case IDC_CAM_VIEW_GLOBAL: - g_camera->SetTrackMode (CAMERA_GLOBALFRAME); - return FALSE; - case IDC_CAM_VIEW_TGTTO: - case IDC_CAM_VIEW_TGTFROM: { - SetFocus(GetDlgItem(hWnd, LOWORD(wParam))); - char cbuf[256]; - GetWindowText (GetDlgItem (hWnd, IDC_CAM_VIEW_REFLIST), cbuf, 256); - Body *obj = g_psys->GetObj (cbuf, true); - if (!obj) obj = g_psys->GetBase (cbuf, true); + pos.x = 75; + pos.y = 100; + ImGui::SetCursorPos(pos); + ImGui::ArrowButton("##right", ImGuiDir_Right); + if(ImGui::IsItemActive()){ + if (rot_is_tilt) g_camera->Rotate (-dt, 0); + else g_camera->ShiftPhi ( dt); + } + + pos.x = 35; + pos.y = 155; + ImGui::SetCursorPos(pos); + if(ImGui::Button("Forward")) { + g_camera->ResetCockpitDir(); + } +} + +void DlgCamera::AddCbodyNode(const CelestialBody *cbody) { + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth; + const bool is_selected = m_SelectedTarget == cbody->Name(); + if (is_selected) + node_flags |= ImGuiTreeNodeFlags_Selected; + if(cbody->nSecondary()) { + bool node_open = ImGui::TreeNodeEx(cbody->Name(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = cbody->Name(); + if(node_open) { + for (int i = 0; i < cbody->nSecondary(); i++) { + AddCbodyNode (cbody->Secondary(i)); + } + ImGui::TreePop(); + } + } else { + ImGui::TreeNodeEx(cbody->Name(), node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = cbody->Name(); + } +} + +void DlgCamera::DrawTarget() { + const ImGuiWindowFlags window_flags = ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX; + + ImGui::BeginChild("ChildL", ImVec2(250, 0), window_flags); + for (int i = 0; i < g_psys->nStar(); i++) + AddCbodyNode (g_psys->GetStar(i)); + + if (ImGui::TreeNode("Spaceports")) { + for (int i = 0; i < g_psys->nGrav(); i++) { + Body *obj = g_psys->GetGravObj (i); + if (obj->Type() != OBJTP_PLANET) continue; + Planet *planet = (Planet*)obj; + if (g_psys->nBase(planet) > 0) { + if(ImGui::TreeNode(planet->Name())) { + for (int j = 0; j < g_psys->nBase(planet); j++) { + const char *name = g_psys->GetBase (planet,j)->Name(); + const bool is_selected = m_SelectedTarget == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name, node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = name; + } + ImGui::TreePop(); + } + } + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Vessels")) { + for (int i = 0; i < g_psys->nVessel(); i++) { + const char *name = g_psys->GetVessel(i)->Name(); + const bool is_selected = m_SelectedTarget == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = name; + } + ImGui::TreePop(); + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + ImGui::Text("Target : %s", m_SelectedTarget.c_str()); + if(ImGui::Button("Apply", button_sz)) { + Body *obj = g_psys->GetObj (m_SelectedTarget.c_str(), true); + if (!obj) obj = g_psys->GetBase (m_SelectedTarget.c_str(), true); + if ( obj) g_pOrbiter->SetView (obj, 1); + } + if(ImGui::Button("Focus Cockpit", button_sz)) { + g_pOrbiter->SetView (g_focusobj, 0); + } + if(ImGui::Button("Focus Extern", button_sz)) { + g_pOrbiter->SetView (g_focusobj, 1); + } + ImGui::EndChild(); +} +void DlgCamera::DrawTrack() { + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX; + ImGui::BeginChild("ChildL", ImVec2(250, 0), window_flags); + { + ImGui::BeginGroupPanel("Moveable modes"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + + if(ImGui::Button("Target Relative", button_sz)) { + g_camera->SetTrackMode (CAMERA_TARGETRELATIVE); + } + if(ImGui::Button("Absolute Direction", button_sz)) { + g_camera->SetTrackMode (CAMERA_ABSDIRECTION); + } + if(ImGui::Button("Global Frame", button_sz)) { + g_camera->SetTrackMode (CAMERA_GLOBALFRAME); + } + ImGui::EndGroupPanel(); + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImGui::BeginGroupPanel("Fixed modes"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + + if(ImGui::Button("Target From...", button_sz)) { + Body *obj = g_psys->GetObj (m_SelectedTarget.c_str(), true); + if (!obj) obj = g_psys->GetBase (m_SelectedTarget.c_str(), true); if (obj && obj != g_camera->Target()) - g_camera->SetTrackMode (LOWORD(wParam) == IDC_CAM_VIEW_TGTTO ? CAMERA_TARGETTOOBJECT : CAMERA_TARGETFROMOBJECT, obj); - } return FALSE; - } - break; - } - return FALSE; -} - - -// ====================================================================== -// ====================================================================== - -TabGround::TabGround (HWND hParentTab): CameraTab (hParentTab, IDD_CAM_PG_GROUND, DlgProc) -{ -} - -// ====================================================================== - -void TabGround::Show (bool show) -{ - EchoCurrentGroundpos (hTab); - CameraTab::Show (show); -} - -// ====================================================================== - -char *TabGround::HelpContext () const -{ - return (char*)"/cam_ground.htm"; -} - -// ====================================================================== - -void TabGround::SelectPlanet (HWND hWnd, int idx) -{ - int i; - char cbuf[256]; - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_GETLBTEXT, idx, (LPARAM)cbuf); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_RESETCONTENT, 0, 0); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_RESETCONTENT, 0, 0); - Planet *planet = g_psys->GetPlanet (cbuf); - if (!planet) return; - - for (i = 0; i < planet->nGroundObserver(); i++) { - const GROUNDOBSERVERSPEC *go = planet->GetGroundObserver (i); - if (SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_FINDSTRINGEXACT, 0, (LPARAM)go->site) == CB_ERR) - SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_ADDSTRING, 0, (LPARAM)go->site); - } - if (i) { - SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_SETCURSEL, 0, 0); - SelectSite (hWnd, 0); - } -} - -// ====================================================================== - -void TabGround::SelectSite (HWND hWnd, int idx) -{ - int i; - char cbuf[256]; - bool found = false; - double lng, lat, alt; - int pidx = SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_GETCURSEL, 0, 0); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_GETLBTEXT, pidx, (LPARAM)cbuf); - Planet *planet = g_psys->GetPlanet (cbuf); - if (!planet) return; - - SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_GETLBTEXT, idx, (LPARAM)cbuf); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_RESETCONTENT, 0, 0); - - for (i = 0; i < planet->nGroundObserver(); i++) { - const GROUNDOBSERVERSPEC *go = planet->GetGroundObserver (i); - if (!strcmp (go->site, cbuf)) { - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_ADDSTRING, 0, (LPARAM)go->addr); - if (!found) { - lng = go->lng; - lat = go->lat; - alt = go->alt; - } - found = true; - } - } - if (found) { - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_SETCURSEL, 0, 0); - EchoGroundpos (hWnd, lng, lat, alt); - } -} - -// ====================================================================== - -void TabGround::SelectAddr (HWND hWnd, int idx) -{ - char cbuf[256], sitestr[256], addrstr[256]; - - int pidx = SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_GETCURSEL, 0, 0); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_GETLBTEXT, pidx, (LPARAM)cbuf); - Planet *planet = g_psys->GetPlanet (cbuf); - if (!planet) return; - - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_SITE), sitestr, 256); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_GETLBTEXT, idx, (LPARAM)addrstr); - const GROUNDOBSERVERSPEC *go = planet->GetGroundObserver (sitestr, addrstr); - if (go) { - EchoGroundpos (hWnd, go->lng, go->lat, go->alt); - } -} - -// ====================================================================== - -void TabGround::EchoCurrentGroundpos (HWND hWnd) -{ - char cbuf[256]; - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, WM_GETTEXT, 256, (LPARAM)cbuf); - Planet *planet = g_psys->GetPlanet (cbuf); - if (!planet) return; - - double lng, lat, alt; - planet->GlobalToEquatorial (*g_camera->GPosPtr(), lng, lat, alt); - alt -= planet->Size() + planet->Elevation (lng, lat); - bool changed = EchoGroundpos (hWnd, lng, lat, alt); - - if (changed) { - SetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_SITE), ""); - SetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_ADDR), ""); - } -} - -// ====================================================================== - -bool TabGround::EchoGroundpos (HWND hWnd, double lng, double lat, double alt) -{ - bool changed = false; - char cbuf[256], ccbuf[256]; - - sprintf (cbuf, "%+0.6f", lng*DEG); - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_LNG), ccbuf, 256); - if (strcmp (cbuf, ccbuf)) { - SetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_LNG), cbuf); - changed = true; - } - sprintf (cbuf, "%+0.6f", lat*DEG); - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_LAT), ccbuf, 256); - if (strcmp (cbuf, ccbuf)) { - SetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_LAT), cbuf); - changed = true; - } - sprintf (cbuf, "%0.4g", alt); - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_ALT), ccbuf, 256); - if (strcmp (cbuf, ccbuf)) { - SetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_ALT), cbuf); - changed = true; - } - return changed; -} - -// ====================================================================== - -void TabGround::ApplyObserver (HWND hWnd) -{ - char cbuf[256], site[256], addr[256]; - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, WM_GETTEXT, 256, (LPARAM)cbuf); - Planet *planet = g_psys->GetPlanet (cbuf); + g_camera->SetTrackMode (CAMERA_TARGETFROMOBJECT, obj); + } + if(ImGui::Button("Target To...", button_sz)) { + Body *obj = g_psys->GetObj (m_SelectedTarget.c_str(), true); + if (!obj) obj = g_psys->GetBase (m_SelectedTarget.c_str(), true); + if (obj && obj != g_camera->Target()) + g_camera->SetTrackMode (CAMERA_TARGETTOOBJECT, obj); + } + for (int i = 0; i < g_psys->nStar(); i++) + AddCbodyNode (g_psys->GetStar(i)); + + if (ImGui::TreeNode("Spaceports")) { + for (int i = 0; i < g_psys->nGrav(); i++) { + Body *obj = g_psys->GetGravObj (i); + if (obj->Type() != OBJTP_PLANET) continue; + Planet *planet = (Planet*)obj; + if (g_psys->nBase(planet) > 0) { + if(ImGui::TreeNode(planet->Name())) { + for (int j = 0; j < g_psys->nBase(planet); j++) { + const char *name = g_psys->GetBase (planet,j)->Name(); + const bool is_selected = m_SelectedTarget == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name, node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = name; + } + ImGui::TreePop(); + } + } + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Vessels")) { + for (int i = 0; i < g_psys->nVessel(); i++) { + const char *name = g_psys->GetVessel(i)->Name(); + const bool is_selected = m_SelectedTarget == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = name; + } + ImGui::TreePop(); + } + ImGui::EndGroupPanel(); + ImGui::EndChild(); +} +void DlgCamera::DrawGround() { + const ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX;; + ImGui::BeginChild("ChildL", ImVec2(250, 0), window_flags); + { + ImGui::BeginGroupPanel("Ground Location"); + for (int i = 0; i < g_psys->nGrav(); i++) { + Body *obj = g_psys->GetGravObj (i); + if (obj->Type() != OBJTP_PLANET) continue; + Planet *planet = (Planet*)obj; + + + if (planet->nGroundObserver() > 0) { + if(ImGui::TreeNode(planet->Name())) { + for (int j = 0; j < planet->nGroundObserver(); j++) { + const GROUNDOBSERVERSPEC *go = planet->GetGroundObserver (j); + std::string name = std::string(go->site) + "-" + go->addr; + const bool is_selected = m_SelectedSite == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name.c_str(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + m_SelectedSite = name; + m_SitePlanet = planet->Name(); + sprintf(longitude, "%+0.6f", go->lng * 180.0/PI); + sprintf(latitude, "%+0.6f", go->lat * 180.0/PI); + sprintf(altitude, "%0.4g", go->alt); + } + } + ImGui::TreePop(); + } + } + } + ImGui::EndGroupPanel(); + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImGui::BeginGroupPanel("Coordinates"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + ImGui::InputText("Longitude", longitude, 64, ImGuiInputTextFlags_CharsDecimal); + ImGui::InputText("Latitude", latitude, 64, ImGuiInputTextFlags_CharsDecimal); + ImGui::InputText("Altitude", altitude, 64, ImGuiInputTextFlags_CharsDecimal); + + ImGui::InputText("Follow Terrain", followterrain, 64, ImGuiInputTextFlags_CharsDecimal); + + if(ImGui::Button("Current", button_sz)) { + const Planet *planet = g_psys->GetPlanet (m_SitePlanet.c_str()); + if(!planet) { + planet = g_camera->ProxyPlanet(); + m_SitePlanet = planet->Name(); + } + if (planet) { + double lng, lat, alt; + planet->GlobalToEquatorial (*g_camera->GPosPtr(), lng, lat, alt); + alt -= planet->Size() + planet->Elevation (lng, lat); + sprintf(longitude, "%+0.6f", lng * 180.0/PI); + sprintf(latitude, "%+0.6f", lat * 180.0/PI); + sprintf(altitude, "%0.4g", alt); + } + SetCurrentGroundpos(); + } + if(ImGui::Button("Apply", button_sz)) { + ApplyObserver(); + } + + ImGui::Checkbox("Target lock", &m_TargetLock); + ImGui::EndGroupPanel(); + ImGui::EndChild(); +} + +void DlgCamera::ApplyObserver() { + Planet *planet = g_psys->GetPlanet (m_SitePlanet.c_str()); if (!planet) return; double lng, lat, alt = 1.7; bool ok; - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_LNG), cbuf, 256); - ok = (sscanf (cbuf, "%lf", &lng) == 1); - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_LAT), cbuf, 256); - ok = ok && (sscanf (cbuf, "%lf", &lat) == 1); - GetWindowText (GetDlgItem (hWnd, IDC_CAM_GRND_ALT), cbuf, 256); - ok = ok && (sscanf (cbuf, "%lf", &alt) == 1); + ok = (sscanf (longitude, "%lf", &lng) == 1); + ok = ok && (sscanf (latitude, "%lf", &lat) == 1); + ok = ok && (sscanf (altitude, "%lf", &alt) == 1); double altlimit; - GetWindowText (GetDlgItem (hWnd, IDC_EDIT1), cbuf, 256); - if (sscanf (cbuf, "%lf", &altlimit)) - g_camera->SetGroundObserver_TerrainLimit (max(1.0,altlimit)); + if (sscanf (followterrain, "%lf", &altlimit)) + g_camera->SetGroundObserver_TerrainLimit (std::max(1.0,altlimit)); if (ok) g_camera->SetGroundMode (CAMERA_GROUNDOBSERVER, planet, lng*RAD, lat*RAD, alt, NULL); - return; - - - SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, WM_GETTEXT, 256, (LPARAM)site); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, WM_GETTEXT, 256, (LPARAM)addr); - - if (sscanf (site, "%lf%lf%lf", &lng, &lat, &alt) >= 2) { - lng *= RAD; lat *= RAD; alt = max (alt, 1.0); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, WM_SETTEXT, 0, (LPARAM)""); - } else { - const GROUNDOBSERVERSPEC *go = planet->GetGroundObserver (site, addr); - if (!go) return; - lng = go->lng, lat = go->lat, alt = go->alt; - } - - Camera::GroundObserverSite gos; - strncpy (gos.planet, cbuf, 63); - strncpy (gos.site, site, 63); - strncpy (gos.addr, addr, 63); - g_camera->SetGroundMode (CAMERA_GROUNDOBSERVER, planet, lng, lat, alt, &gos); } -// ====================================================================== - -void TabGround::SetCurrentGroundpos (HWND hWnd) -{ - EchoCurrentGroundpos (hWnd); +void DlgCamera::SetCurrentGroundpos() { if (g_camera->GetExtMode() == CAMERA_GROUNDOBSERVER) return; // nothing to do - ApplyObserver (hWnd); - if (SendDlgItemMessage (hWnd, IDC_CAM_GRND_LOCK, BM_GETCHECK, 0, 0) == BST_UNCHECKED) { + ApplyObserver (); + + if (!m_TargetLock) { // force lock to target to initialise camera direction g_camera->SetGroundObserver_TargetLock (true); g_camera->SetGroundObserver_TargetLock (false); } - char cbuf[256]; double alt; - GetWindowText (GetDlgItem (hWnd, IDC_EDIT1), cbuf, 256); - if (sscanf (cbuf, "%lf", &alt)) - g_camera->SetGroundObserver_TerrainLimit (max(1.0,alt)); -} - -// ====================================================================== - -void TabGround::GObserverChanged (const char *str) -{ - if (active) - EchoCurrentGroundpos (hTab); -} - -// ====================================================================== - -void TabGround::LockChanged (bool lock) -{ - SendDlgItemMessage (hTab, IDC_CAM_GRND_LOCK, BM_SETCHECK, lock ? BST_CHECKED : BST_UNCHECKED, 0); -} - -// ====================================================================== - -BOOL TabGround::Init (HWND hWnd) -{ - int i, j; - - for (i = 0; i < g_psys->nPlanet(); i++) { - Planet *planet = g_psys->GetPlanet(i); - if (planet->isMoon()) continue; - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_ADDSTRING, 0, (LPARAM)planet->Name()); - for (j = 0; j < planet->nSecondary(); j++) - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_ADDSTRING, 0, (LPARAM)planet->Secondary(j)->Name()); - } - SelectPlanet (hWnd, 0); - - // restore last ground observer address - const Camera::GroundObserverSite *gos = g_camera->GetGroundObserverSite(); - if (gos->planet[0]) { - i = SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_FINDSTRINGEXACT, -1, (LPARAM)gos->planet); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_SETCURSEL, i, 0); - SelectPlanet (hWnd, i); - i = SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_FINDSTRINGEXACT, -1, (LPARAM)gos->site); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_SETCURSEL, i, 0); - SelectSite (hWnd, i); - i = SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_FINDSTRINGEXACT, -1, (LPARAM)gos->addr); - SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_SETCURSEL, i, 0); - } else { // nothing stored; use planet closest to camera instead - i = SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_FINDSTRINGEXACT, -1, (LPARAM)g_camera->ProxyPlanet()->Name()); - if (i == CB_ERR) i = 0; - SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_SETCURSEL, i, 0); - SelectPlanet (hWnd, i); - } - - SendDlgItemMessage (hWnd, IDC_CAM_GRND_LOCK, BM_SETCHECK, (WPARAM)g_camera->GroundObserver_TargetLock(), 0); - - char cbuf[256]; - sprintf (cbuf, "%0.0lf", g_camera->GroundObserver_TerrainLimit()); - SetWindowText (GetDlgItem (hWnd, IDC_EDIT1), cbuf); - - return TRUE; -} - -// ====================================================================== - -INT_PTR CALLBACK TabGround::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraTab::DlgProcInit (hWnd, uMsg, wParam, lParam); - TabGround *pTab = (TabGround*)(uMsg == WM_INITDIALOG ? lParam : GetWindowLongPtr (hWnd, DWLP_USER)); - - switch (uMsg) { - case WM_INITDIALOG: - return pTab->Init (hWnd); - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_CAM_GRND_REFBODY: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int idx = SendDlgItemMessage (hWnd, IDC_CAM_GRND_REFBODY, CB_GETCURSEL, 0, 0); - pTab->SelectPlanet (hWnd, idx); - } - return TRUE; - case IDC_CAM_GRND_SITE: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int idx = SendDlgItemMessage (hWnd, IDC_CAM_GRND_SITE, CB_GETCURSEL, 0, 0); - pTab->SelectSite (hWnd, idx); - } - return TRUE; - case IDC_CAM_GRND_ADDR: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int idx = SendDlgItemMessage (hWnd, IDC_CAM_GRND_ADDR, CB_GETCURSEL, 0, 0); - pTab->SelectAddr (hWnd, idx); - } - return TRUE; - case IDC_CAM_GRND_CURRENT: - pTab->SetCurrentGroundpos (hWnd); - return TRUE; - case IDC_CAM_GRND_APPLY: - pTab->ApplyObserver (hWnd); - return TRUE; - case IDC_CAM_GRND_LOCK: { - int idx = SendDlgItemMessage (hWnd, IDC_CAM_GRND_LOCK, BM_GETCHECK, 0, 0); - g_camera->SetGroundObserver_TargetLock (idx == BST_CHECKED ? true : false); - } return TRUE; - case IDC_EDIT1: { - char cbuf[256]; - double alt; - GetWindowText (GetDlgItem (hWnd, IDC_EDIT1), cbuf, 256); - if (sscanf (cbuf, "%lf", &alt)) g_camera->SetGroundObserver_TerrainLimit (alt); - } return TRUE; - } - break; - } - return FALSE; -} - + if (sscanf (followterrain, "%lf", &alt)) + g_camera->SetGroundObserver_TerrainLimit (std::max(1.0,alt)); +} + +void DlgCamera::DrawFoV() { + float fov=oapiCameraAperture()/RAD/0.5; + if(ImGui::SliderFloat("Field of View", &fov, 10.0, 90.0, "%.1f")) { + if(fov < 10.0f) fov = 10.0f; + else if(fov>90.0f) fov = 90.0f; + oapiCameraSetAperture(fov*RAD*0.5); + } +} +void DlgCamera::DrawPreset() { + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX;;; + static float sz1 = 0.0; + float sz2; + // ImGui::Splitter(true, 0.5f, 8.0f, &sz1, &sz2, 8, 8, ImGui::GetContentRegionAvail().y); + ImGui::BeginChild("ChildL", ImVec2(250, 0), window_flags); + { + if (ImGui::BeginListBox("##listbox preset", ImVec2(ImGui::GetContentRegionAvail().x*0.99, ImGui::GetContentRegionAvail().y*0.99))) + { + for (int i = 0; i < g_camera->nPreset(); i++) { + char buf[256]; + g_camera->GetPreset(i)->GetDescr (buf, 256); + + const bool is_selected = (m_SelectedPreset == i); + ImGui::PushID(i); + if (ImGui::Selectable(buf, is_selected)) + m_SelectedPreset = i; + ImGui::PopID(); + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndListBox(); + //ImGui::Text("Selected %d", m_SelectedPreset); + } + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + if(ImGui::Button("Add", button_sz)) { + g_camera->AddPreset(); + } + if(ImGui::Button("Delete", button_sz)) { + g_camera->DelPreset(m_SelectedPreset); + } + if(ImGui::Button("Clear", button_sz)) { + g_camera->ClearPresets(); + } + if(ImGui::Button("Recall", button_sz)) { + g_camera->RecallPreset (m_SelectedPreset); + } + + ImGui::EndChild(); -// ====================================================================== -// ====================================================================== - -TabFov::TabFov (HWND hParentTab): CameraTab (hParentTab, IDD_CAM_PG_FOV, DlgProc) -{ -} - -// ====================================================================== - -char *TabFov::HelpContext () const -{ - return (char*)"/cam_fov.htm"; -} - -// ====================================================================== - -void TabFov::FovChanged (double fov) -{ - RegisterFov (hTab, fov); -} - -// ====================================================================== - -void TabFov::RegisterFov (HWND hWnd, double fov) -{ - char cbuf[128]; - SendDlgItemMessage (hWnd, IDC_CAM_FOV_SLIDER, TBM_SETPOS, TRUE, (LONG)fov); - GetWindowText (GetDlgItem (hWnd, IDC_CAM_FOV_INPUT), cbuf, 127); - if (fabs (fov-atof(cbuf)) > 1e-5) { - sprintf (cbuf, "%0.2f", fov); - SetWindowText (GetDlgItem (hWnd, IDC_CAM_FOV_INPUT), cbuf); - } -} - -// ====================================================================== - -void TabFov::SetFov (double fov_deg) -{ - if (fov_deg >= 10.0 && fov_deg <= 90.0) - g_pOrbiter->SetFOV (fov_deg * 0.5*RAD); -} - -// ====================================================================== - -INT_PTR CALLBACK TabFov::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraTab::DlgProcInit (hWnd, uMsg, wParam, lParam); - TabFov *pTab = (TabFov*)(uMsg == WM_INITDIALOG ? lParam : GetWindowLongPtr(hWnd,DWLP_USER)); - - switch (uMsg) { - case WM_INITDIALOG: - // initialise the slider - SendDlgItemMessage (hWnd, IDC_CAM_FOV_SLIDER, TBM_SETRANGE, FALSE, MAKELONG(10,90)); - SendDlgItemMessage (hWnd, IDC_CAM_FOV_SLIDER, TBM_SETPAGESIZE, 0, 10L); - SendDlgItemMessage (hWnd, IDC_CAM_FOV_SLIDER, TBM_SETTICFREQ, 10, 0); - // set controls to current FOV setting - pTab->RegisterFov (hWnd, g_camera->Aperture()*2.0*DEG); - return TRUE; - case WM_COMMAND: - switch (LOWORD (wParam)) { - case IDC_CAM_FOV_30: - case IDC_CAM_FOV_40: - case IDC_CAM_FOV_50: - case IDC_CAM_FOV_60: - case IDC_CAM_FOV_70: - pTab->SetFov ((LOWORD(wParam)-IDC_CAM_FOV_30+3)*10.0); - return TRUE; - } - case IDC_CAM_FOV_INPUT: - if (HIWORD (wParam) == EN_UPDATE) { - char cbuf[256]; - GetWindowText ((HWND)lParam, cbuf, 256); - pTab->SetFov (atof(cbuf)); - return TRUE; - } - break; - break; - case WM_HSCROLL: // trackbar notifications - if (LOWORD (wParam) == TB_THUMBTRACK) { - pTab->SetFov ((double)SendDlgItemMessage (hWnd, IDC_CAM_FOV_SLIDER, TBM_GETPOS, 0, 0)); - return TRUE; - } - break; - } - return FALSE; -} - - -// ====================================================================== -// ====================================================================== - -TabPreset::TabPreset (HWND hParentTab): CameraTab (hParentTab, IDD_CAM_PG_PRESET, DlgProc) -{ -} - -// ====================================================================== - -char *TabPreset::HelpContext () const -{ - return (char*)"/cam_preset.htm"; -} - -// ====================================================================== - -INT_PTR CALLBACK TabPreset::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraTab::DlgProcInit (hWnd, uMsg, wParam, lParam); - - DWORD i, j; - char cbuf[256]; - - switch (uMsg) { - case WM_INITDIALOG: - for (i = 0; i < g_camera->nPreset(); i++) { - g_camera->GetPreset(i)->GetDescr (cbuf, 256); - SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_ADDSTRING, 0, (LPARAM)cbuf); - } - if (i) SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_SETCURSEL, 0, 0); - return TRUE; - - case WM_COMMAND: - switch (LOWORD (wParam)) { - case IDHELP: - DefHelpContext.topic = (char*)"/cam_preset.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDC_CAM_PRE_NEW: - i = g_camera->AddPreset(); - g_camera->GetPreset(i)->GetDescr (cbuf, 256); - SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_ADDSTRING, 0, (LPARAM)cbuf); - SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_SETCURSEL, i, 0); - return TRUE; - case IDC_CAM_PRE_DEL: - i = SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_GETCURSEL, 0, 0); - if (i != LB_ERR && g_camera->DelPreset (i)) { - SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_DELETESTRING, i, 0); - j = SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_GETCOUNT, 0, 0); - if (j > i) SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_SETCURSEL, i, 0); - else if (i > 0) SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_SETCURSEL, i-1, 0); - } - return TRUE; - case IDC_CAM_PRE_RECALL: - i = SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_GETCURSEL, 0, 0); - if (i != LB_ERR) g_camera->RecallPreset (i); - return TRUE; - case IDC_CAM_PRE_CLEAR: - g_camera->ClearPresets(); - SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_RESETCONTENT, 0, 0); - return TRUE; - case IDC_CAM_PRE_LIST: - if (HIWORD(wParam) == LBN_DBLCLK) { - i = SendDlgItemMessage (hWnd, IDC_CAM_PRE_LIST, LB_GETCURSEL, 0, 0); - if (i != LB_ERR) g_camera->RecallPreset (i); - return TRUE; - } - break; - } - break; - } - return FALSE; } diff --git a/Src/Orbiter/DlgCamera.h b/Src/Orbiter/DlgCamera.h index c66c58920..7e5ba9814 100644 --- a/Src/Orbiter/DlgCamera.h +++ b/Src/Orbiter/DlgCamera.h @@ -8,166 +8,56 @@ #ifndef __DLGCAMERA_H #define __DLGCAMERA_H -#include "DialogWin.h" -#include "CommCtrl.h" -#include "Celbody.h" +#include "DlgMgr.h" #include "Camera.h" -class CameraTab; -class TabControl; -class TabFov; -class TabGround; - -// ====================================================================== - -class DlgCamera: public DialogWin { +class CelestialBody; +class DlgCamera : public ImGuiDialog { public: - DlgCamera (HINSTANCE hInstance, HWND hParent, void *context); - ~DlgCamera (); - void Update (); - void ModeChanged (); - void FovChanged (double fov); - void GObserverChanged (const char *str); - void Refresh (const Camera *cam); - char *HelpContext () const { return hcontext; } - BOOL OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl); - BOOL OnNotify (HWND hDlg, int idCtrl, LPNMHDR pnmh); - BOOL OnApp (HWND hDlg, WPARAM wParam, LPARAM lParam); - -protected: - void Clear (); - int AddTab (HWND hDlg, CameraTab *tab, const char *label); - void SwitchTab (HWND hDlg); - + DlgCamera(); + void OnDraw() override; private: - int nTab; - CameraTab **pTab; - TabControl *pTabControl; - TabFov *pTabFov; - TabGround *pTabGround; - char *hcontext; -}; - -// ====================================================================== -// Base class for camera dialog tabs - -class CameraTab { -public: - CameraTab (HWND hParentTab, int dlgId, DLGPROC dlgProc); - virtual ~CameraTab (); - virtual void Show (bool show); - virtual void Update () {} - virtual char *HelpContext() const = 0; - -protected: - static INT_PTR CALLBACK DlgProcInit (HWND, UINT, WPARAM, LPARAM); - HWND hParent; - HWND hTab; - bool active; -}; + void DrawControl(); + void DrawTarget(); + void DrawTrack(); + void DrawGround(); + void DrawFoV(); + void DrawPreset(); -// ====================================================================== -// Camera control tab - -class TabControl: public CameraTab { -public: - TabControl (HWND hParentTab); - char *HelpContext () const; - void Show (bool show); - void Update (); - void ModeChanged (); - BOOL Init (HWND hWnd); - -protected: - void GetCamMode (); - void SetLayout (); - static INT_PTR CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM); ExtCamMode extmode; IntCamMode intmode; bool extcam; bool ground_lock; bool rot_is_tilt, rot_is_pan; -}; - -// ====================================================================== -// Camera target selection tab - -class TabTarget: public CameraTab { -public: - TabTarget (HWND hParentTab); - char *HelpContext () const; - void Show (bool show); - BOOL Init (HWND hWnd); - -protected: - void AddCbodyNode (HWND hWnd, const CelestialBody *cbody, HTREEITEM parent); - void AddVessels (HWND hWnd); - void AddSurfbases (HWND hWnd); - static INT_PTR CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM); -}; + std::string m_SelectedTarget; + std::string m_SelectedSite; + std::string m_SitePlanet; + int m_SelectedPreset; + + char longitude[64]=""; + char latitude[64]=""; + char altitude[64]=""; + char followterrain[64]=""; + bool m_TargetLock; + + void AddCbodyNode(const CelestialBody *body); + void ApplyObserver(); + void SetCurrentGroundpos(); + + struct CameraTab { + const char *name; + const char *helptopic; + void (DlgCamera::* func)(); + }; + + static inline CameraTab tabs[] = { + {"Control", "/cam_control.htm", &DlgCamera::DrawControl}, + {"Target", "/cam_target.htm", &DlgCamera::DrawTarget}, + {"Track", "/cam_track.htm", &DlgCamera::DrawTrack}, + {"Ground", "/cam_ground.htm", &DlgCamera::DrawGround}, + {"FoV", "/cam_fov.htm", &DlgCamera::DrawFoV}, + {"Preset", "/cam_preset.htm", &DlgCamera::DrawPreset}, + }; -// ====================================================================== -// Camera track mode tab - -class TabView: public CameraTab { -public: - TabView (HWND hParentTab); - char *HelpContext () const; - BOOL Init (HWND hWnd); - -protected: - static INT_PTR CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM); -}; - -// ====================================================================== -// Camera ground mode tab - -class TabGround: public CameraTab { -public: - TabGround (HWND hParentTab); - void GObserverChanged (const char *str); - void LockChanged (bool lock); - void Show (bool show); - char *HelpContext () const; - BOOL Init (HWND hWnd); - -protected: - void SelectPlanet (HWND hWnd, int idx); - void SelectSite (HWND hWnd, int idx); - void SelectAddr (HWND hWnd, int idx); - void ApplyObserver (HWND hWnd); - void SetCurrentGroundpos (HWND hWnd); - bool EchoGroundpos (HWND hWnd, double lng, double lat, double alt); - void EchoCurrentGroundpos (HWND hWnd); - static INT_PTR CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM); }; - -// ====================================================================== -// Camera field of view tab - -class TabFov: public CameraTab { -public: - TabFov (HWND hParentTab); - void FovChanged (double fov); - char *HelpContext () const; - -protected: - void RegisterFov (HWND hWnd, double fov); - void SetFov (double fov_deg); - static INT_PTR CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM); -}; - -// ====================================================================== -// Camera preset position management tab - -class TabPreset: public CameraTab { -public: - TabPreset (HWND hParentTab); - char *HelpContext () const; - -protected: - static INT_PTR CALLBACK DlgProc (HWND, UINT, WPARAM, LPARAM); -}; - #endif // !__DLGCAMERA_H \ No newline at end of file diff --git a/Src/Orbiter/DlgCapture.cpp b/Src/Orbiter/DlgCapture.cpp index c853c0cb5..24c69fadd 100644 --- a/Src/Orbiter/DlgCapture.cpp +++ b/Src/Orbiter/DlgCapture.cpp @@ -5,186 +5,76 @@ // Screen capture dialog // ====================================================================== -#define STRICT 1 - -#include #include "DlgCapture.h" #include "Orbiter.h" -#include "Resource.h" -#include "Resource2.h" -#include +#include "IconsFontAwesome6.h" extern Orbiter *g_pOrbiter; -extern HELPCONTEXT DefHelpContext; // ====================================================================== -DlgCapture::DlgCapture (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_CAPTURE, 0, 0, context) -{ -} - -// ====================================================================== +#include "imgui.h" +#include "imgui_extras.h" -DlgCapture::~DlgCapture () -{ +DlgCapture::DlgCapture() : ImGuiDialog(ICON_FA_VOICEMAIL " Orbiter: Capture frames",{323,343}) { + SetHelp("html/orbiter.chm", "/capture.htm"); } -// ====================================================================== - -BOOL DlgCapture::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - const char *fmtstr[4] = {".bmp",".png",".jpg",".tif"}; - char cbuf[256]; - int i; +void DlgCapture::OnDraw() { CFG_CAPTUREPRM &prm = g_pOrbiter->Cfg()->CfgCapturePrm; - SendDlgItemMessage (hDlg, IDC_CAP_TOCLIP, BM_SETCHECK, prm.ImageTgt == 0 ? BST_CHECKED : BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_CAP_TOFILE, BM_SETCHECK, prm.ImageTgt == 0 ? BST_UNCHECKED : BST_CHECKED, 0); - EnableWindow (GetDlgItem (hDlg, IDC_CAP_FNAME), prm.ImageTgt == 0 ? FALSE:TRUE); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_FNAME), prm.ImageFile); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_DNAME), prm.SequenceDir); - sprintf (cbuf, "%05d", prm.SequenceStart); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_STARTCOUNT), cbuf); - sprintf (cbuf, "%d", prm.SequenceSkip); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_SKIPFRAME), cbuf); - - SendDlgItemMessage (hDlg, IDC_COMBO1, CB_RESETCONTENT, 0, 0); - for (i = 0; i < 4; i++) - SendDlgItemMessage (hDlg, IDC_COMBO1, CB_INSERTSTRING, -1, (LPARAM)fmtstr[i]); - SendDlgItemMessage (hDlg, IDC_COMBO1, CB_SETCURSEL, prm.ImageFormat, 0); - EnableWindow (GetDlgItem (hDlg, IDC_COMBO2), prm.ImageFormat == 2 ? TRUE:FALSE); - SendDlgItemMessage (hDlg, IDC_COMBO2, CB_RESETCONTENT, 0, 0); - for (i = 1; i <= 10; i++) { - SendDlgItemMessage(hDlg, IDC_COMBO2, CB_INSERTSTRING, -1, (LPARAM)std::to_string(i).data()); - } - SendDlgItemMessage (hDlg, IDC_COMBO2, CB_SETCURSEL, prm.ImageQuality-1, 0); - - return TRUE; -} - -// ====================================================================== - -BOOL DlgCapture::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDCANCEL: - Take (hDlg); - g_pOrbiter->CloseDialog (hDlg); - return TRUE; - case IDC_COMBO1: - if (code == CBN_SELCHANGE) { - int idx = SendDlgItemMessage (hDlg, id, CB_GETCURSEL, 0, 0); - g_pOrbiter->Cfg()->CfgCapturePrm.ImageFormat = idx; - EnableWindow (GetDlgItem (hDlg, IDC_COMBO2), idx == 2 ? TRUE:FALSE); - return TRUE; - } - break; - case IDC_COMBO2: - if (code == CBN_SELCHANGE) { - int idx = SendDlgItemMessage (hDlg, id, CB_GETCURSEL, 0, 0); - g_pOrbiter->Cfg()->CfgCapturePrm.ImageQuality = idx+1; - return TRUE; + ImGui::BeginGroupPanel("Save snapshots"); + ImGui::RadioButton("To the clipboard", &prm.ImageTgt, 0); + ImGui::RadioButton("To file", &prm.ImageTgt, 1); + if(prm.ImageTgt == 1) { + ImGui::SameLine(); + ImGui::InputText("##File", prm.ImageFile, 128); } - break; - case IDC_CAP_SNAPSHOT: { - oapi::GraphicsClient *gclient = g_pOrbiter->GetGraphicsClient(); - if (gclient) { - Take (hDlg); - if (g_pOrbiter->Cfg()->CfgCapturePrm.ImageTgt) { - oapi::ImageFileFormat fmt = (oapi::ImageFileFormat)g_pOrbiter->Cfg()->CfgCapturePrm.ImageFormat; - float quality = (float)g_pOrbiter->Cfg()->CfgCapturePrm.ImageQuality/10.0f; - gclient->clbkSaveSurfaceToImage (0, g_pOrbiter->Cfg()->CfgCapturePrm.ImageFile, fmt, quality); - AutoIncrement (hDlg); - GetWindowText (GetDlgItem (hDlg, IDC_CAP_FNAME), g_pOrbiter->Cfg()->CfgCapturePrm.ImageFile, 128); - } else + if(ImGui::Button("Take snapshot")) { + oapi::GraphicsClient *gclient = g_pOrbiter->GetGraphicsClient(); + if (prm.ImageTgt) { + oapi::ImageFileFormat fmt = (oapi::ImageFileFormat)prm.ImageFormat; + float quality = (float)prm.ImageQuality/10.0f; + gclient->clbkSaveSurfaceToImage (0, prm.ImageFile, fmt, quality); + AutoIncrement (prm.ImageFile); + } else { gclient->clbkSaveSurfaceToImage (0, NULL, oapi::IMAGE_BMP); + } } - } return TRUE; - case IDC_CAP_RECORD: - if (g_pOrbiter->IsCapturingFrames()) { - g_pOrbiter->StopCaptureFrames(); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_RECORD), "Start recording"); - } else if (Take (hDlg)) { - g_pOrbiter->StartCaptureFrames(); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_RECORD), "Stop recording"); - } - return TRUE; - case IDC_BUTTON1: - g_pOrbiter->Cfg()->SetDefaults_Capture(); - OnInitDialog (hDlg, 0, 0); - return TRUE; - case IDHELP: { - char *topic = (char*)"/capture.htm"; - DefHelpContext.topic = topic; - g_pOrbiter->OpenHelp (&DefHelpContext); - } return TRUE; - case IDC_CAP_TOCLIP: - case IDC_CAP_TOFILE: - if (code == BN_CLICKED) { - Take (hDlg); - return TRUE; - } - break; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); -} + ImGui::EndGroupPanel(); -// ====================================================================== + ImGui::BeginGroupPanel("Save frame sequence"); + ImGui::InputText("To directory", prm.SequenceDir, 128); + ImGui::InputInt("Counter start", &prm.SequenceStart); + ImGui::InputInt("Skip frames", &prm.SequenceSkip); -void DlgCapture::Update () -{ - if (g_pOrbiter->IsCapturingFrames()) { - char cbuf[256]; - int curframe = g_pOrbiter->Cfg()->CfgCapturePrm.SequenceStart; - int showframe; - GetWindowText (GetDlgItem (hWnd, IDC_CAP_STARTCOUNT), cbuf, 256); - if (!sscanf (cbuf, "%d", &showframe) || showframe != curframe) { - sprintf (cbuf, "%05d", curframe); - SetWindowText (GetDlgItem (hWnd, IDC_CAP_STARTCOUNT), cbuf); + if (g_pOrbiter->IsCapturingFrames()) { + if(ImGui::Button("Stop recording")) + g_pOrbiter->StopCaptureFrames(); + } else if (ImGui::Button("Start recording")) { + g_pOrbiter->StartCaptureFrames(); } - } -} + ImGui::EndGroupPanel(); -// ====================================================================== + ImGui::BeginGroupPanel("File settings"); + const char* const formats[] = { "BMP", "PNG", "JPG", "TIF" }; + ImGui::Combo("File format", &prm.ImageFormat, formats, IM_ARRAYSIZE(formats)); + ImGui::BeginDisabled(prm.ImageFormat != 2); + ImGui::SliderInt("Image quality", &prm.ImageQuality, 1, 10); + ImGui::EndDisabled(); + ImGui::EndGroupPanel(); -bool DlgCapture::Take (HWND hDlg) -{ - bool ok = true; - char cbuf[256]; - CFG_CAPTUREPRM &prm = g_pOrbiter->Cfg()->CfgCapturePrm; - bool isClipbd = (SendDlgItemMessage (hDlg, IDC_CAP_TOCLIP, BM_GETCHECK, 0, 0) == TRUE); - prm.ImageTgt = (isClipbd ? 0 : 1); - EnableWindow (GetDlgItem (hDlg, IDC_CAP_FNAME), isClipbd ? FALSE:TRUE); - GetWindowText (GetDlgItem (hDlg, IDC_CAP_FNAME), prm.ImageFile, 128); - GetWindowText (GetDlgItem (hDlg, IDC_CAP_DNAME), prm.SequenceDir, 128); - GetWindowText (GetDlgItem (hDlg, IDC_CAP_STARTCOUNT), cbuf, 256); - if (!sscanf (cbuf, "%d", &prm.SequenceStart)) { - prm.SequenceStart = 0; - ok = false; - } - GetWindowText (GetDlgItem (hDlg, IDC_CAP_SKIPFRAME), cbuf, 256); - if (!sscanf (cbuf, "%d", &prm.SequenceSkip)) { - prm.SequenceSkip = 0; - ok = false; - } - return ok; + if(ImGui::Button("Reset")) + g_pOrbiter->Cfg()->SetDefaults_Capture(); } -// ====================================================================== - -bool DlgCapture::AutoIncrement (HWND hDlg) +void DlgCapture::AutoIncrement (char *cbuf) { - char cbuf[256]; - GetWindowText (GetDlgItem (hDlg, IDC_CAP_FNAME), cbuf, 256); int i, count, len = strlen(cbuf); for (i = len; i > 0; i--) if (cbuf[i-1] == '\\') break; if (sscanf (cbuf+i, "%d", &count) == 1) { int w = len-i; sprintf (cbuf+i, "%0*d", w, count+1); - SetWindowText (GetDlgItem (hDlg, IDC_CAP_FNAME), cbuf); - return true; } - return false; -} \ No newline at end of file +} diff --git a/Src/Orbiter/DlgCapture.h b/Src/Orbiter/DlgCapture.h index 3e293414c..4985df820 100644 --- a/Src/Orbiter/DlgCapture.h +++ b/Src/Orbiter/DlgCapture.h @@ -8,22 +8,15 @@ #ifndef __DLGCAPTURE_H #define __DLGCAPTURE_H -#include "DialogWin.h" - +#include "OrbiterAPI.h" // ====================================================================== // Class for screen capture dialog -class DlgCapture: public DialogWin { +class DlgCapture : public ImGuiDialog { + void AutoIncrement(char *); public: - DlgCapture (HINSTANCE hInstance, HWND hParent, void *context); - ~DlgCapture (); - void Update (); - BOOL OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl); - -protected: - bool Take (HWND hDlg); - bool AutoIncrement (HWND hDlg); + DlgCapture(); + void OnDraw() override; }; #endif // !__DLGCAPTURE_H \ No newline at end of file diff --git a/Src/Orbiter/DlgFocus.cpp b/Src/Orbiter/DlgFocus.cpp index fc18e3c1c..74c3f0ad5 100644 --- a/Src/Orbiter/DlgFocus.cpp +++ b/Src/Orbiter/DlgFocus.cpp @@ -5,571 +5,223 @@ // Focus vessel selection dialog // ====================================================================== -#define STRICT 1 - #include "DlgFocus.h" -#include "Resource.h" -#include "Orbiter.h" #include "Psys.h" -#include "SuperVessel.h" -#include "Pane.h" +#include "OrbiterAPI.h" +#include "Orbiter.h" #include "Camera.h" -#include "Util.h" -#include "Uxtheme.h" -#include +#include "imgui.h" +#include +#include "IconsFontAwesome6.h" -extern Orbiter *g_pOrbiter; +extern Camera *g_camera; extern PlanetarySystem *g_psys; +extern Orbiter *g_pOrbiter; extern Vessel *g_focusobj, *g_pfocusobj; -extern Camera *g_camera; -extern HELPCONTEXT DefHelpContext; -extern char DBG_MSG[256]; - -// ====================================================================== - -DlgFocus::DlgFocus (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_JUMPVESSEL, 0, 0, context) -{ - pos = &g_pOrbiter->Cfg()->CfgWindowPos.DlgFocus; - irange = g_pOrbiter->Cfg()->CfgUIPrm.SelVesselRange; - unroll_assemblies = g_pOrbiter->Cfg()->CfgUIPrm.bSelVesselFlat; -} - -// ====================================================================== - -DlgFocus::~DlgFocus () -{ - g_pOrbiter->Cfg()->CfgUIPrm.SelVesselTab = ctab; - g_pOrbiter->Cfg()->CfgUIPrm.SelVesselRange = irange; - g_pOrbiter->Cfg()->CfgUIPrm.bSelVesselFlat = unroll_assemblies; -} - -// ====================================================================== - -BOOL DlgFocus::OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - EnableThemeDialogTexture (hWnd, ETDT_ENABLETAB); - - const int ntab = 4; - const char *label[ntab] = {"All", "Nearby", "Location", "Class"}; - TC_ITEM tie; - tie.mask = TCIF_TEXT; - tie.iImage = -1; - for (int i = 0; i < ntab; i++) { - tie.pszText = (char*)label[i]; - SendDlgItemMessage (hWnd, IDC_TAB1, TCM_INSERTITEM, i, (LPARAM)&tie); - } - ctab = g_pOrbiter->Cfg()->CfgUIPrm.SelVesselTab; - - GetClientRect (hWnd, &rClient); - rTab = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_TAB1)); - rEdit = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_EDIT1)); - rTree = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_TREE1)); - rAppl = GetClientPos (hWnd, GetDlgItem (hWnd, IDOK)); - rPrev = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_BUTTON1)); - rHelp = GetClientPos (hWnd, GetDlgItem (hWnd, IDHELP)); - rClse = GetClientPos (hWnd, GetDlgItem (hWnd, IDCANCEL)); - rSldr = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_SLIDER1)); - rLabl = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_STATIC1)); - rChck = GetClientPos (hWnd, GetDlgItem (hWnd, IDC_CHECK1)); - - SetWindowText (GetDlgItem (hWnd, IDC_EDIT1), g_focusobj->Name()); - TabCtrl_SetCurSel (GetDlgItem (hWnd, IDC_TAB1), ctab); - SwitchTab (hWnd); - EnableWindow (GetDlgItem (hWnd, IDC_BUTTON1), g_pfocusobj ? TRUE:FALSE); - EnableWindow (GetDlgItem (hWnd, IDOK), FALSE); - - SendDlgItemMessage (hWnd, IDC_CHECK1, BM_SETCHECK, unroll_assemblies ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hWnd, IDC_SLIDER1, TBM_SETRANGE, FALSE, MAKELONG(1,8)); - SendDlgItemMessage (hWnd, IDC_SLIDER1, TBM_SETPAGESIZE, 0, 1L); - SendDlgItemMessage (hWnd, IDC_SLIDER1, TBM_SETTICFREQ, 1, 0); - SendDlgItemMessage (hWnd, IDC_SLIDER1, TBM_SETPOS, TRUE, (LONG)irange); - SetRangeLabel (hWnd, irange); - - return TRUE; -} - -// ====================================================================== - -BOOL DlgFocus::OnSize (HWND hDlg, WPARAM wParam, int w, int h) -{ - RECT r; - int i; - const int nSizeElm = 9; - const int SizeElm[nSizeElm] = { - IDC_EDIT1, IDC_TREE1, IDOK, IDC_BUTTON1, IDC_SLIDER1, IDC_STATIC1, IDC_CHECK1, IDCANCEL, IDHELP - }; - - GetClientRect (hDlg, &r); - int dw = r.right - rClient.right; - int dh = r.bottom - rClient.bottom; - for (i = 0; i < nSizeElm; i++) - ShowWindow (GetDlgItem (hDlg, SizeElm[i]), SW_HIDE); - r = rTab; r.right += dw; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_TAB1), r); - r = rEdit; r.right += dw; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_EDIT1), r); - r = rTree; r.right += dw; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_TREE1), r); - r = rAppl; r.left += dw; r.right += dw; - SetClientPos (hDlg, GetDlgItem (hDlg, IDOK), r); - r = rPrev; r.left += dw; r.right += dw; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_BUTTON1), r); - r = rClse; r.left += dw; r.right += dw; r.top += dh; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDCANCEL), r); - r = rHelp; r.left += dw; r.right += dw; r.top += dh; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDHELP), r); - r = rSldr; r.left += dw; r.right += dw; r.top += dh; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_SLIDER1), r); - r = rLabl; r.left += dw; r.right += dw; r.top += dh; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_STATIC1), r); - r = rChck; r.left += dw; r.right += dw; r.top += dh; r.bottom += dh; - SetClientPos (hDlg, GetDlgItem (hDlg, IDC_CHECK1), r); - for (i = 0; i < nSizeElm; i++) { - if (ctab != 1 && (SizeElm[i] == IDC_SLIDER1 || SizeElm[i] == IDC_STATIC1)) continue; - if (ctab == 3 && SizeElm[i] == IDC_CHECK1) continue; - ShowWindow (GetDlgItem (hDlg, SizeElm[i]), SW_SHOW); - } - return DialogWin::OnSize (hDlg, wParam, w, h); -} - -// ====================================================================== - -BOOL DlgFocus::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDC_EDIT1: - if (code == EN_CHANGE) WatchEdit (hDlg); - return TRUE; - case IDC_CHECK1: - if (code == BN_CLICKED) WatchAssemblyUnroll (hDlg); - return TRUE; - case IDC_BUTTON1: - SelectPreviousVessel (hDlg); - return TRUE; - case IDHELP: - DefHelpContext.topic = (char*)"/selvessel.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDOK: - return SelectVessel (hDlg); - } - return DialogWin::OnCommand (hDlg, id, code, hControl); -} - -// ====================================================================== - -BOOL DlgFocus::OnNotify (HWND hDlg, int idCtrl, LPNMHDR pnmh) -{ - switch (pnmh->idFrom) { - case IDC_TAB1: - return OnNotifyTab (hDlg, pnmh); - case IDC_TREE1: - return OnNotifyTree (hDlg, pnmh); - } - return FALSE; -} - -// ====================================================================== - -BOOL DlgFocus::OnNotifyTab (HWND hDlg, LPNMHDR pnmh) -{ - if (pnmh->code == TCN_SELCHANGE) SwitchTab (hDlg); - return FALSE; -} - -// ====================================================================== - -BOOL DlgFocus::OnNotifyTree (HWND hDlg, LPNMHDR pnmh) -{ - NM_TREEVIEW *pnmtv = (NM_TREEVIEW FAR *)pnmh; - if (pnmtv->hdr.code == TVN_SELCHANGED) { - HTREEITEM hti = pnmtv->itemNew.hItem; - bool ok = true; - if (ctab >= 2) - ok = (SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hti) != 0); - if (ok) { - TVITEM tvi; - char cbuf[256]; - tvi.hItem = hti; - tvi.pszText = cbuf; - tvi.cchTextMax = 256; - tvi.mask = TVIF_TEXT; - BOOL res = SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETITEM, 0, (LPARAM)&tvi); - Vessel *vessel = g_psys->GetVessel (cbuf, true); - if (!vessel) vessel = g_focusobj; - strcpy (cbuf, vessel->Name()); - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), cbuf); - EnableWindow (GetDlgItem (hDlg, IDOK), vessel == g_focusobj ? FALSE : TRUE); - } - } - return FALSE; -} - -// ====================================================================== - -BOOL DlgFocus::OnHScroll (HWND hDlg, WORD request, WORD curpos, HWND hControl) -{ - int irange_old = irange; - irange = SendDlgItemMessage (hDlg, IDC_SLIDER1, TBM_GETPOS, 0, 0); - if (irange != irange_old) { - SetRangeLabel (hDlg, irange); - RescanTree_Nearby (hDlg); - return TRUE; - } - return DialogWin::OnHScroll (hDlg, request, curpos, hControl); -} - -// ====================================================================== - -void DlgFocus::SetRangeLabel (HWND hDlg, int irange) -{ - char cbuf[256] = "Range: "; - if (irange == 1) strcpy (cbuf+7, "0.1"); - else if (irange <= 4) { - int i, e=1; - for (i = 2; i < irange; i++) e *= 10; - sprintf (cbuf+7, "%d", e); - } else sprintf (cbuf+7, "1E%d", irange-2); - strcat (cbuf, " km"); - SetWindowText (GetDlgItem (hDlg, IDC_STATIC1), cbuf); -} - -// ====================================================================== - -void DlgFocus::WatchEdit (HWND hDlg) -{ - char cbuf[256]; - GetWindowText (GetDlgItem (hDlg, IDC_EDIT1), cbuf, 256); - Vessel *vessel = g_psys->GetVessel (cbuf, true); - EnableWindow (GetDlgItem (hDlg, IDOK), vessel && vessel != g_focusobj ? TRUE:FALSE); -} - -// ====================================================================== - -void DlgFocus::WatchAssemblyUnroll (HWND hDlg) -{ - bool old_unroll = unroll_assemblies; - unroll_assemblies = (SendDlgItemMessage (hDlg, IDC_CHECK1, BM_GETCHECK, 0, 0) == BST_CHECKED); - if (unroll_assemblies != old_unroll) { - SwitchTab (hDlg); - } -} - -// ====================================================================== - -BOOL DlgFocus::OnUserMessage (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - EnableWindow (GetDlgItem (hWnd, IDC_BUTTON1), g_pfocusobj ? TRUE:FALSE); - switch (wParam) { - case MSG_CREATEVESSEL: - case MSG_KILLVESSEL: - SwitchTab (hDlg); - return TRUE; - case MSG_FOCUSVESSEL: - UpdateFocus (hDlg); - return TRUE; - } - return FALSE; -} - -// ====================================================================== - -void DlgFocus::SwitchTab (HWND hDlg) -{ - HWND hTab = GetDlgItem (hDlg, IDC_TAB1); - HWND hTree = GetDlgItem (hDlg, IDC_TREE1); - ctab = TabCtrl_GetCurSel (hTab); - - LONG ws = GetWindowLongPtr (hTree, GWL_STYLE); - if (ctab < 2) ws &= ~TVS_LINESATROOT; - else ws |= TVS_LINESATROOT; - SetWindowLongPtr (hTree, GWL_STYLE, ws); - - switch (ctab) { - case 0: - RescanTree_All (hDlg); - break; - case 1: - RescanTree_Nearby (hDlg); - break; - case 2: - RescanTree_Location (hDlg); - break; - case 3: - RescanTree_Class (hDlg); - break; - } - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), g_focusobj->Name()); - - int showrange = (ctab == 1 ? SW_SHOW : SW_HIDE); - ShowWindow (GetDlgItem (hDlg, IDC_STATIC1), showrange); - ShowWindow (GetDlgItem (hDlg, IDC_SLIDER1), showrange); - ShowWindow (GetDlgItem (hDlg, IDC_CHECK1), ctab == 3 ? SW_HIDE : SW_SHOW); -} - -// ====================================================================== - -void DlgFocus::RescanTree_All (HWND hDlg) -{ - char cbuf[256]; cbuf[255] = '\0'; - HTREEITEM hti, hti_focus = NULL; - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_DELETEITEM, 0, 0); - for (DWORD i = 0; i < g_psys->nVessel(); i++) { - Vessel *vessel = g_psys->GetVessel(i); - if (vessel->GetEnableFocus()) { - hti = AddVesselToTree (hDlg, NULL, vessel); - if (hti) hti_focus = hti; - } - } - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hti_focus); -} - -// ====================================================================== - -void DlgFocus::RescanTree_Nearby (HWND hDlg) -{ - double range = pow(10.0,(double)(irange+1)); - char cbuf[256]; cbuf[255] = '\0'; - HTREEITEM hti, hti_focus = NULL; - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_DELETEITEM, 0, 0); - const Vector &campos = g_camera->GPos(); - for (DWORD i = 0; i < g_psys->nVessel(); i++) { - Vessel *vessel = g_psys->GetVessel(i); - if (vessel->GetEnableFocus()) { - double dst = campos.dist (vessel->GPos()); - if (dst <= range) { - hti = AddVesselToTree (hDlg, NULL, vessel); - if (hti) hti_focus = hti; - } - } - } - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hti_focus); -} - -// ====================================================================== - -void DlgFocus::RescanTree_Location (HWND hDlg) -{ - HTREEITEM hti, hti_ref, hti_focus = NULL; - TV_INSERTSTRUCT tvis; - TVITEM tvi; - char cbuf[256]; cbuf[255] = '\0'; - - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_DELETEITEM, 0, 0); - - tvis.hParent = NULL; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.cChildren = 1; - - tvi.mask = TVIF_TEXT; - tvi.pszText = cbuf; - tvi.cchTextMax = 255; - - for (DWORD i = 0; i < g_psys->nVessel(); i++) { +DlgFocus::DlgFocus() : ImGuiDialog(ICON_FA_ROCKET " Orbiter: Select spacecraft", {300, 320}) { + SetHelp("html/orbiter.chm", "/selvessel.htm"); +} + +void DlgFocus::OnDraw() { + const char *tabs[] = { + "All", "Nearby", "Location", "Class" + }; + + void (DlgFocus::* func[])() = { + &DlgFocus::DrawAll, &DlgFocus::DrawNearby, &DlgFocus::DrawLocation, &DlgFocus::DrawClass + }; + + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; + if (ImGui::BeginTabBar("FocusTabBar", tab_bar_flags)) + { + for(size_t i = 0; i*func[i])(); + ImGui::EndTabItem(); + } + } + ImGui::EndTabBar(); + } +} + +void DlgFocus::DrawAll() { + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX; + ImGui::Text("Spacecraft : %s", m_SelectedShip.c_str()); + ImGui::BeginChild("ChildL", ImVec2(150, 0), true, window_flags); + + for (int i = 0; i < g_psys->nVessel(); i++) { + Vessel *vessel = g_psys->GetVessel(i); + if (vessel->GetEnableFocus()) { + std::string name = vessel->Name(); + const bool is_selected = m_SelectedShip == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(vessel->Name(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + m_SelectedShip = vessel->Name(); + } + } + } + + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + if(ImGui::Button("Select", button_sz)) { + Vessel *vessel = g_psys->GetVessel (m_SelectedShip.c_str(), true); + if (vessel) { + g_pOrbiter->SetFocusObject (vessel); + } + } + if(ImGui::Button("Previous", button_sz)) { + if (g_pfocusobj) { + g_pOrbiter->SetFocusObject (g_pfocusobj); + m_SelectedShip = g_pfocusobj->Name(); + } + } + ImGui::EndChild(); +} +void DlgFocus::DrawNearby() { + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX; + ImGui::Text("Spacecraft : %s", m_SelectedShip.c_str()); + ImGui::BeginChild("ChildL", ImVec2(150, 0), true, window_flags); + + const Vector &campos = g_camera->GPos(); + for (int i = 0; i < g_psys->nVessel(); i++) { + Vessel *vessel = g_psys->GetVessel(i); + if (vessel->GetEnableFocus()) { + double dst = campos.dist (vessel->GPos()); + if (dst <= m_Range * 1000.0f) { + std::string name = vessel->Name(); + const bool is_selected = m_SelectedShip == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + char cbuf[128]; + sprintf(cbuf,"%s (%.1fkm)", vessel->Name(), dst/1000.0f); + ImGui::TreeNodeEx(cbuf, node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + m_SelectedShip = vessel->Name(); + } + } + } + } + + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + if(ImGui::Button("Select", button_sz)) { + Vessel *vessel = g_psys->GetVessel (m_SelectedShip.c_str(), true); + if (vessel) { + g_pOrbiter->SetFocusObject (vessel); + } + } + if(ImGui::Button("Previous", button_sz)) { + if (g_pfocusobj) { + g_pOrbiter->SetFocusObject (g_pfocusobj); + m_SelectedShip = g_pfocusobj->Name(); + } + } + + ImGui::SetNextItemWidth(-FLT_MIN); + if(ImGui::SliderFloat("##slider warp", &m_Range, 1.0f, 1000000.0f, "%.1fkm", ImGuiSliderFlags_Logarithmic)) { + } + + ImGui::EndChild(); +} +void DlgFocus::DrawLocation() { + std::map> vesselMap; + for (int i = 0; i < g_psys->nVessel(); i++) { Vessel *vessel = g_psys->GetVessel(i); if (vessel->GetEnableFocus()) { const CelestialBody *ref = vessel->ElRef(); - // enter under reference body - hti_ref = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_ROOT, 0); - while (hti_ref) { - tvi.hItem = hti_ref; - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETITEM, 0, (LPARAM)&tvi); - if (!strcmp (tvi.pszText, ref->Name())) - break; - hti_ref = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hti_ref); - } - if (!hti_ref) { - tvis.item.pszText = (char*)ref->Name(); - hti_ref = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_INSERTITEM, 0, (LPARAM)&tvis); - } - hti = AddVesselToTree (hDlg, hti_ref, vessel); - if (hti) hti_focus = hti; - } - } - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hti_focus); -} - -// ====================================================================== - -void DlgFocus::RescanTree_Class (HWND hDlg) -{ - HTREEITEM hti, hti_ref, hti_focus = NULL; - TV_INSERTSTRUCT tvis; - TVITEM tvi; - char cbuf[256]; cbuf[255] = '\0'; - - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_DELETEITEM, 0, 0); - - tvis.hParent = NULL; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.cChildren = 1; - - tvi.mask = TVIF_TEXT; - tvi.pszText = cbuf; - tvi.cchTextMax = 255; - - for (DWORD i = 0; i < g_psys->nVessel(); i++) { + vesselMap[ref->Name()].push_back(vessel); + } + } + + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX; + ImGui::Text("Spacecraft : %s", m_SelectedShip.c_str()); + ImGui::BeginChild("ChildL", ImVec2(150,0), true, window_flags); + + for(auto &kw : vesselMap) { + if(ImGui::TreeNodeEx(kw.first)) { + for(auto &vessel: kw.second) { + const bool is_selected = m_SelectedShip == vessel->Name(); + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(vessel->Name(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + m_SelectedShip = vessel->Name(); + } + } + + ImGui::TreePop(); + } + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + if(ImGui::Button("Select", button_sz)) { + Vessel *vessel = g_psys->GetVessel (m_SelectedShip.c_str(), true); + if (vessel) { + g_pOrbiter->SetFocusObject (vessel); + } + } + if(ImGui::Button("Previous", button_sz)) { + if (g_pfocusobj) { + g_pOrbiter->SetFocusObject (g_pfocusobj); + m_SelectedShip = g_pfocusobj->Name(); + } + } + + ImGui::EndChild(); +} +void DlgFocus::DrawClass() { + std::map> vesselMap; + for (int i = 0; i < g_psys->nVessel(); i++) { Vessel *vessel = g_psys->GetVessel(i); if (vessel->GetEnableFocus()) { - // enter under reference body - hti_ref = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_ROOT, 0); - while (hti_ref) { - tvi.hItem = hti_ref; - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETITEM, 0, (LPARAM)&tvi); - if (!_stricmp (tvi.pszText, vessel->ClassName())) - break; - hti_ref = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hti_ref); - } - if (!hti_ref) { - strncpy (cbuf, vessel->ClassName(), 255); - tvis.item.pszText = cbuf; - hti_ref = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_INSERTITEM, 0, (LPARAM)&tvis); - } - hti = AddVesselToTree (hDlg, hti_ref, vessel); - if (hti) hti_focus = hti; - } - } - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hti_focus); -} - -// ====================================================================== - -HTREEITEM DlgFocus::AddVesselToTree (HWND hDlg, HTREEITEM hti, Vessel *vessel, bool force) -{ - bool unroll = (unroll_assemblies || ctab == 3); - if (!unroll && vessel->isAttached() && !force) return NULL; - HTREEITEM hti_focus = NULL; - DWORD i; - - SuperVessel *assembly = NULL; - if (!unroll && (assembly = vessel->SuperStruct())) { - char cbuf[256], assembly_name[256]; - Vessel *vessel0 = assembly->GetVessel(0); - sprintf (assembly_name, "%s [assembly]", vessel0->Name()); - TVITEM tvi; - tvi.mask = TVIF_TEXT; - tvi.pszText = cbuf; - tvi.cchTextMax = 255; - HTREEITEM hti_assembly = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hti); - while (hti_assembly) { - tvi.hItem = hti_assembly; - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETITEM, 0, (LPARAM)&tvi); - if (!_stricmp (tvi.pszText, assembly_name)) - break; - hti_assembly = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hti_assembly); - } - if (!hti_assembly) { - TV_INSERTSTRUCT tvis; - tvis.hParent = hti; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.cChildren = 1; - tvis.item.pszText = assembly_name; - hti_assembly = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_INSERTITEM, 0, (LPARAM)&tvis); - } - hti = hti_assembly; - } - TV_INSERTSTRUCT tvis; - tvis.hParent = hti; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_TEXT | TVIF_CHILDREN; - tvis.item.pszText = (char*)vessel->Name(); - tvis.item.cChildren = 0; - if (!unroll) { - for (i = 0; ; i++) { - AttachmentSpec *as = vessel->GetAttachmentFromIndex (false, i); - if (!as) break; - if (as->mate) {tvis.item.cChildren = 1; break; } - } - } - HTREEITEM hti_vessel = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_INSERTITEM, 0, (LPARAM)&tvis); - if (tvis.item.cChildren) { - for (i = 0; ; i++) { - AttachmentSpec *as = vessel->GetAttachmentFromIndex (false, i); - if (!as) break; - if (as->mate) { - HTREEITEM hti_child = AddVesselToTree (hDlg, hti_vessel, as->mate, true); - if (hti_child) hti_focus = hti_child; - } - } - } - return (vessel == g_focusobj ? hti_vessel : hti_focus); -} - -// ====================================================================== - -BOOL DlgFocus::SelectVessel (HWND hDlg) -{ - char cbuf[256]; - GetWindowText (GetDlgItem (hDlg, IDC_EDIT1), cbuf, 256); - Vessel *vessel = g_psys->GetVessel (cbuf, true); - if (vessel) { - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), vessel->Name()); - g_pOrbiter->SetFocusObject (vessel); - HTREEITEM hti = FindItem (hDlg, vessel->Name(), 0); - if (hti) - PostMessage (GetDlgItem (hDlg, IDC_TREE1), TVM_SELECTITEM, TVGN_CARET, (LPARAM)hti); - } else { - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), g_focusobj->Name()); - MessageBeep (MB_ICONEXCLAMATION); - } - WatchEdit (hDlg); - return TRUE; + vesselMap[vessel->ClassName()].push_back(vessel); + } + } + + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX; + ImGui::Text("Spacecraft : %s", m_SelectedShip.c_str()); + ImGui::BeginChild("ChildL", ImVec2(150,0), true, window_flags); + + for(auto &kw : vesselMap) { + if(ImGui::TreeNodeEx(kw.first.c_str())) { + for(auto &vessel: kw.second) { + const bool is_selected = m_SelectedShip == vessel->Name(); + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(vessel->Name(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + m_SelectedShip = vessel->Name(); + } + } + + ImGui::TreePop(); + } + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR"); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + if(ImGui::Button("Select", button_sz)) { + Vessel *vessel = g_psys->GetVessel (m_SelectedShip.c_str(), true); + if (vessel) { + g_pOrbiter->SetFocusObject (vessel); + } + } + if(ImGui::Button("Previous", button_sz)) { + if (g_pfocusobj) { + g_pOrbiter->SetFocusObject (g_pfocusobj); + m_SelectedShip = g_pfocusobj->Name(); + } + } + + ImGui::EndChild(); } - -// ====================================================================== - -void DlgFocus::SelectPreviousVessel (HWND hDlg) -{ - if (g_pfocusobj) { - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), g_pfocusobj->Name()); - SelectVessel (hDlg); - } -} - -// ====================================================================== - -void DlgFocus::UpdateFocus (HWND hDlg) -{ - char cbuf[256]; - GetWindowText (GetDlgItem (hDlg, IDC_EDIT1), cbuf, 256); - if (strcmp (cbuf, g_focusobj->Name())) { - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), g_focusobj->Name()); - HTREEITEM hti = FindItem (hDlg, g_focusobj->Name(), 0); - if (hti) - PostMessage (GetDlgItem (hDlg, IDC_TREE1), TVM_SELECTITEM, TVGN_CARET, (LPARAM)hti); - } -} - -// ====================================================================== - -HTREEITEM DlgFocus::FindItem (HWND hDlg, const char *str, HTREEITEM hti_first) -{ - HTREEITEM hti_match = NULL; - TVITEM tvi; - - char cbuf[256]; cbuf[255] = '\0'; - tvi.mask = TVIF_TEXT | TVIF_CHILDREN; - tvi.pszText = cbuf; - tvi.cchTextMax = 255; - - if (!hti_first) - hti_first = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_ROOT, 0); - - do { - tvi.hItem = hti_first; - SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETITEM, 0, (LPARAM)&tvi); - if (tvi.cChildren) { - hti_match = FindItem (hDlg, str, (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hti_first)); - if (hti_match) return hti_match; - } else { - if (!strcmp (cbuf, str)) return hti_first; - } - hti_first = (HTREEITEM)SendDlgItemMessage (hDlg, IDC_TREE1, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hti_first); - } while (hti_first); - return NULL; -} \ No newline at end of file diff --git a/Src/Orbiter/DlgFocus.h b/Src/Orbiter/DlgFocus.h index 933a072d2..9d9a3eca2 100644 --- a/Src/Orbiter/DlgFocus.h +++ b/Src/Orbiter/DlgFocus.h @@ -8,42 +8,18 @@ #ifndef __DLGFOCUS_H #define __DLGFOCUS_H -#include "DialogWin.h" -#include "CommCtrl.h" +#include "OrbiterAPI.h" -class DlgFocus: public DialogWin { +class DlgFocus : public ImGuiDialog { public: - DlgFocus (HINSTANCE hInstance, HWND hParent, void *context); - ~DlgFocus (); - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnSize (HWND hDlg, WPARAM wParam, int w, int h); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - BOOL OnUserMessage (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnNotify (HWND hDlg, int idCtrl, LPNMHDR pnmh); - BOOL OnHScroll (HWND hDlg, WORD request, WORD curpos, HWND hControl); - -protected: - BOOL OnNotifyTab (HWND hDlg, LPNMHDR pnmh); - BOOL OnNotifyTree (HWND hDlg, LPNMHDR pnmh); - void SwitchTab (HWND hDlg); - void RescanTree_All (HWND hDlg); - void RescanTree_Nearby (HWND hDlg); - void RescanTree_Location (HWND hDlg); - void RescanTree_Class (HWND hDlg); - HTREEITEM AddVesselToTree (HWND hDlg, HTREEITEM hti, Vessel *vessel, bool force = false); - BOOL SelectVessel (HWND hDlg); - void SelectPreviousVessel (HWND hDlg); - void UpdateFocus (HWND hDlg); - void WatchEdit (HWND hDlg); - void WatchAssemblyUnroll (HWND hDlg); - void SetRangeLabel (HWND hDlg, int irange); - HTREEITEM FindItem (HWND hDlg, const char *str, HTREEITEM hti_first); - -private: - int ctab; - int irange; - bool unroll_assemblies; - RECT rClient, rTab, rEdit, rTree, rAppl, rPrev, rHelp, rClse, rSldr, rLabl, rChck; + DlgFocus(); + void OnDraw() override; + void DrawAll(); + void DrawNearby(); + void DrawLocation(); + void DrawClass(); + std::string m_SelectedShip; + float m_Range = 1.0; }; #endif // !__DLGFOCUS_H \ No newline at end of file diff --git a/Src/Orbiter/DlgFunction.cpp b/Src/Orbiter/DlgFunction.cpp index bcff7a976..0c80f91b9 100644 --- a/Src/Orbiter/DlgFunction.cpp +++ b/Src/Orbiter/DlgFunction.cpp @@ -5,84 +5,30 @@ // Custom function selection dialog // ====================================================================== -#define STRICT 1 - #include "DlgFunction.h" #include "Orbiter.h" -#include "Resource.h" -#include "Resource2.h" +#include "imgui.h" +#include "IconsFontAwesome6.h" extern Orbiter *g_pOrbiter; -extern HELPCONTEXT DefHelpContext; - -// ====================================================================== - -DlgFunction::DlgFunction (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_CUSTOMCMD, 0, 0, context) -{ -} - -// ====================================================================== - -void DlgFunction::ScanFunctions (HWND hDlg) -{ - DWORD i, idx; - SendDlgItemMessage (hDlg, IDC_CUSTOMCMD_LIST, LB_RESETCONTENT, 0, 0); - for (i = 0; i < g_pOrbiter->ncustomcmd; i++) - idx = SendDlgItemMessage (hDlg, IDC_CUSTOMCMD_LIST, LB_ADDSTRING, 0, (LPARAM)g_pOrbiter->customcmd[i].label); - if (i) SendDlgItemMessage (hDlg, IDC_CUSTOMCMD_LIST, LB_SETCURSEL, 0, 0); -} - -// ====================================================================== -void DlgFunction::RunFunction (HWND hDlg) -{ - int idx = SendDlgItemMessage (hDlg, IDC_CUSTOMCMD_LIST, LB_GETCURSEL, 0, 0); - if (idx != LB_ERR) g_pOrbiter->customcmd[idx].func (g_pOrbiter->customcmd[idx].context); +DlgFunction::DlgFunction() : ImGuiDialog(ICON_FA_PUZZLE_PIECE " Orbiter: Custom functions", {340, 290}) { + SetHelp("html/orbiter.chm", "/customcmd.htm"); } -// ====================================================================== - -void DlgFunction::ShowDescription (HWND hDlg) -{ - int idx = SendDlgItemMessage (hDlg, IDC_CUSTOMCMD_LIST, LB_GETCURSEL, 0, 0); - if (idx != LB_ERR) - SetWindowText (GetDlgItem (hDlg, IDC_CUSTOMCMD_DESCR), g_pOrbiter->customcmd[idx].desc); -} - -// ====================================================================== - -BOOL DlgFunction::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - ScanFunctions (hDlg); - ShowDescription (hDlg); - return TRUE; -} - -// ====================================================================== - -BOOL DlgFunction::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDHELP: - DefHelpContext.topic = (char*)"/customcmd.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDOK: - RunFunction (hDlg); - g_pOrbiter->CloseDialog (hDlg); - return TRUE; - case IDC_CUSTOMCMD_LIST: - switch (code) { - case LBN_DBLCLK: - RunFunction (hDlg); - g_pOrbiter->CloseDialog (hDlg); - return TRUE; - case LBN_SELCHANGE: - ShowDescription (hDlg); - return TRUE; - } - break; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); +void DlgFunction::OnDraw() { + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + for (int i = 0; i < g_pOrbiter->ncustomcmd; i++) { + if(ImGui::Button(g_pOrbiter->customcmd[i].label, button_sz)) { + g_pOrbiter->customcmd[i].func (g_pOrbiter->customcmd[i].context); + } + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(g_pOrbiter->customcmd[i].desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + } } diff --git a/Src/Orbiter/DlgFunction.h b/Src/Orbiter/DlgFunction.h index 16c9fac57..bd638e23a 100644 --- a/Src/Orbiter/DlgFunction.h +++ b/Src/Orbiter/DlgFunction.h @@ -8,18 +8,11 @@ #ifndef __DLGFUNCTION_H #define __DLGFUNCTION_H -#include "DialogWin.h" +#include "OrbiterAPI.h" -class DlgFunction: public DialogWin { +class DlgFunction : public ImGuiDialog { public: - DlgFunction (HINSTANCE hInstance, HWND hParent, void *context); - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - -protected: - void ScanFunctions (HWND hDlg); - void RunFunction (HWND hDlg); - void ShowDescription (HWND hDlg); + DlgFunction(); + void OnDraw() override; }; - #endif // !__DLGFUNCTION_H \ No newline at end of file diff --git a/Src/Orbiter/DlgHelp.cpp b/Src/Orbiter/DlgHelp.cpp index 5da8fd73b..fd0e65d77 100644 --- a/Src/Orbiter/DlgHelp.cpp +++ b/Src/Orbiter/DlgHelp.cpp @@ -4,264 +4,29 @@ // ====================================================================== // Help window // ====================================================================== - -#define STRICT 1 - #include "DlgHelp.h" -#include "DlgMgr.h" -#include "Orbiter.h" -#include "Resource.h" #include #include +#include "imgui.h" -extern Orbiter *g_pOrbiter; -extern HELPCONTEXT DefHelpContext; - -static char helpf[256] = "html/orbiter.chm"; -static char deftopic[256] = "html/orbiter.chm::/intro.htm"; -static char vstopic[256] = ""; -static char jmp2url[256] = ""; -static char topic[256] = "/intro.htm"; -static DWORD_PTR pTopic = (DWORD_PTR)topic; -static bool bScnHelp = false; -static bool bVsHelp = false; -static HWND hHelpClient = NULL; - -// ====================================================================== - -DlgHelp::DlgHelp (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_HELP, (DLGPROC)DlgProc, 0, context) -{ - hfooter = 0; - RegisterClientClass (hInstance); -} - -// ====================================================================== - -DlgHelp::~DlgHelp () -{ - UnregisterClientClass (hInst); -} - -// ====================================================================== - -void DlgHelp::RegisterClientClass (HINSTANCE hInstance) -{ - // Register help window class - WNDCLASS wndClass; - wndClass.style = CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = ClientProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = hInstance; - wndClass.hIcon = NULL; - wndClass.hCursor = NULL; - wndClass.hbrBackground = (HBRUSH)GetStockObject (HOLLOW_BRUSH); - wndClass.lpszMenuName = NULL; - wndClass.lpszClassName = "OrbiterHelp"; - RegisterClass (&wndClass); -} - -// ====================================================================== - -void DlgHelp::UnregisterClientClass (HINSTANCE hInstance) -{ - UnregisterClass ("OrbiterHelp", hInstance); -} - -// ====================================================================== - -void DlgHelp::SetScenarioHelp (const char *_helpf) -{ - if (_helpf) { - char cbuf[256]; - strcpy (cbuf, _helpf); - sprintf (helpf, "html/Scenarios/%s.chm", strtok (cbuf, ",")); - if (_access(helpf,0) == 0) { - sprintf (deftopic, "%s::/%s.htm", helpf, strtok (NULL, ",")); - pTopic = 0; - bScnHelp = true; - return; - } - } - strcpy (helpf, "html/orbiter.chm"); - strcpy (deftopic, "html/orbiter.chm::/intro.htm"); - pTopic = (DWORD_PTR)topic; - bScnHelp = false; -} - -// ====================================================================== - -void DlgHelp::SetVesselHelp (const char *_helpf) -{ - if (_helpf) { - char cbuf[256]; - strcpy (cbuf, _helpf); - sprintf (vstopic, "html/Vessels/%s.chm", strtok (cbuf, ",")); - sprintf (jmp2url, "%s::/%s.htm", vstopic, strtok (NULL, ",")); - bVsHelp = true; - } else { - bVsHelp = false; - } -} - -// ====================================================================== +// This is just a placeholder to call the HtmlHelp API +// We draw nothing ourselves +DlgHelp::DlgHelp():ImGuiDialog("Orbiter: Help") {} +void DlgHelp::Display() {} +void DlgHelp::OnDraw() {} -void DlgHelp::InitHelp (HWND hWnd, HELPCONTEXT *hcontext) +void DlgHelp::OpenHelp(const HELPCONTEXT *hc) { - HH_WINTYPE hhwt; - memset (&hhwt, 0, sizeof(hhwt)); - hhwt.cbStruct = sizeof (hhwt); - hhwt.fUniCodeStrings = FALSE; - hhwt.pszType = "Default"; - hhwt.fsValidMembers = HHWIN_PARAM_PROPERTIES | HHWIN_PARAM_TB_FLAGS | - HHWIN_PARAM_STYLES | HHWIN_PARAM_EXSTYLES; - hhwt.fsWinProperties = HHWIN_PROP_TRI_PANE | HHWIN_PROP_NOTITLEBAR | - HHWIN_PROP_TAB_SEARCH | HHWIN_PROP_AUTO_SYNC | HHWIN_PROP_NODEF_STYLES; - hhwt.pszCaption = "Orbiter Help"; - hhwt.dwStyles = WS_CHILD | WS_VISIBLE; - hhwt.dwExStyles = WS_EX_NOPARENTNOTIFY; - hhwt.nShowState = WS_VISIBLE; - hhwt.hwndHelp = NULL; - hhwt.hwndCaller = hWnd; - hhwt.hwndToolBar = NULL; - hhwt.hwndNavigation = NULL; - hhwt.hwndHTML = NULL; - hhwt.iNavWidth = 0; - if (hcontext) { - hhwt.pszToc = (hcontext->toc ? hcontext->toc : "html/orbiter.chm::/Orbiter.hhc"); - hhwt.pszIndex = (hcontext->index ? hcontext->index : "html/orbiter.chm::/Orbiter.hhk"); - hhwt.pszFile = hcontext->helpfile; - hhwt.pszHome = "html/orbiter.chm::/intro.htm"; - } else { - hhwt.pszToc = "html/orbiter.chm::/Orbiter.hhc"; - hhwt.pszIndex = "html/orbiter.chm::/Orbiter.hhk"; - hhwt.pszFile = helpf; - hhwt.pszHome = "html/orbiter.chm::/intro.htm"; - } - hhwt.fsToolBarFlags = HHWIN_BUTTON_EXPAND | HHWIN_BUTTON_BACK | - HHWIN_BUTTON_FORWARD | HHWIN_BUTTON_HOME; - if (bScnHelp) hhwt.fsToolBarFlags |= HHWIN_BUTTON_JUMP1; - if (bVsHelp) hhwt.fsToolBarFlags |= HHWIN_BUTTON_JUMP2; - hhwt.fNotExpanded = FALSE; - hhwt.curNavType = 0; - hhwt.tabpos = 0; - hhwt.idNotify = 0; - hhwt.cHistory = 0; - hhwt.pszJump1 = "Scenario"; - hhwt.pszJump2 = "Vessel"; - hhwt.pszUrlJump1 = deftopic; - hhwt.pszUrlJump2 = jmp2url; - hhwt.rcMinSize.left = 0; - hhwt.rcMinSize.right = 0; - hhwt.rcMinSize.top = 0; - hhwt.rcMinSize.bottom = 0; - - HtmlHelp (hWnd, NULL, HH_SET_WIN_TYPE, (DWORD_PTR)&hhwt); -} - -// ====================================================================== + char buf[256]; + HWND hWnd = (HWND)(ImGui::GetMainViewport()->PlatformHandleRaw); + if(hc->topic) + snprintf(buf, 256, "%s::%s", hc->helpfile, hc->topic); + else + snprintf(buf, 256, "%s", hc->helpfile); -BOOL DlgHelp::OnInitdialog (HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - RECT r, rh; - GetClientRect (hWnd, &r); - dlgw = r.right, dlgh = r.bottom; - GetWindowRect (GetDlgItem (hWnd, IDC_CUSTOM1), &rh); - hfooter = r.bottom - (rh.bottom-rh.top); - GetWindowRect (GetDlgItem (hWnd, IDCANCEL), &rh); - pClose.x = rh.left, pClose.y = rh.top; - ScreenToClient (hWnd, &pClose); - - return OnRequest (hWnd, wParam, lParam); -} - -// ====================================================================== - -BOOL DlgHelp::OnRequest (HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - SendDlgItemMessage (hWnd, IDC_CUSTOM1, WM_USER, wParam, lParam); - return TRUE; -} + buf[255] = '\0'; -// ====================================================================== - -BOOL DlgHelp::OnSize (HWND hWnd, WPARAM wParam, int w, int h) -{ - RECT r; - GetClientRect (hWnd, &r); - int dx = r.right - dlgw, dy = r.bottom - dlgh; - SetWindowPos (GetDlgItem (hWnd, IDC_CUSTOM1), HWND_TOP, 0, 0, r.right, r.bottom-hfooter, SWP_SHOWWINDOW); - SetWindowPos (GetDlgItem (hWnd, IDCANCEL), HWND_TOP, pClose.x+dx, pClose.y+dy, 0, 0, SWP_NOSIZE); - return DialogWin::OnSize (hWnd, wParam, w, h); -} - -// ====================================================================== - -INT_PTR CALLBACK DlgHelp::DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_INITDIALOG: - return ((DlgHelp*)GetDialogWin (hDlg))->OnInitdialog (hDlg, wParam, lParam); - case WM_USER+1: - return ((DlgHelp*)GetDialogWin (hDlg))->OnRequest (hDlg, wParam, lParam); - case WM_SIZE: - return ((DlgHelp*)GetDialogWin (hDlg))->OnSize (hDlg, wParam, LOWORD (lParam), HIWORD (lParam)); - case WM_COMMAND: - switch (LOWORD (wParam)) { - case IDHELP: - DefHelpContext.topic = (char*)"/help.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDCANCEL: - g_pOrbiter->CloseDialog (hDlg); - return TRUE; - } - break; + if(!HtmlHelp (hWnd, buf, HH_DISPLAY_TOPIC, NULL)) { + oapiAddNotification(OAPINOTIF_ERROR, "Failed to open help", buf); } - return OrbiterDefDialogProc (hDlg, uMsg, wParam, lParam); } - -// ====================================================================== - -LRESULT FAR PASCAL DlgHelp::ClientProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT r; - - switch (msg) { - case WM_CREATE: - //InitHelp (hwnd); - return 0; - case WM_USER: { - char cbuf[256]; - if (lParam == 0) { - DlgHelp::InitHelp (hwnd); - strcpy (cbuf, helpf); strcat (cbuf, ">Default"); - hHelpClient = HtmlHelp (hwnd, cbuf, HH_DISPLAY_TOPIC, pTopic); - } else { - HELPCONTEXT *hcontext = (HELPCONTEXT*)lParam; - InitHelp (hwnd, hcontext); - strcpy (cbuf, hcontext->helpfile); - strcat (cbuf, ">Default"); - hHelpClient = HtmlHelp (hwnd, cbuf, HH_DISPLAY_TOPIC, (DWORD_PTR)hcontext->topic); - } - GetClientRect (hwnd, &r); - SetWindowPos (hHelpClient, HWND_TOP, 0, 0, r.right, r.bottom, 0); - } break; - case WM_SIZE: - case WM_PAINT: - GetClientRect (hwnd, &r); - SetWindowPos (hHelpClient, HWND_TOP, 0, 0, r.right, r.bottom, SWP_HIDEWINDOW); - SetWindowPos (hHelpClient, HWND_TOP, 0, 0, r.right, r.bottom, SWP_SHOWWINDOW); - // there must be something better! (everything else I tried produces artefacts) - break; - case WM_NOTIFY: - // if (wParam == NEWTOPIC) { - // InitHelp (hwnd); - // hHelpClient = HtmlHelp (hwnd, "orbiter.chm>Default", HH_DISPLAY_TOPIC, (DWORD)topic); - // } - return TRUE; - } - return DefWindowProc (hwnd, msg, wParam, lParam); -} - diff --git a/Src/Orbiter/DlgHelp.h b/Src/Orbiter/DlgHelp.h index a862fe39e..65f8b7820 100644 --- a/Src/Orbiter/DlgHelp.h +++ b/Src/Orbiter/DlgHelp.h @@ -7,11 +7,12 @@ #ifndef __DLGHELP_H #define __DLGHELP_H +#include "OrbiterAPI.h" -#include "DialogWin.h" +//#include "DialogWin.h" // ====================================================================== - +/* class DlgHelp: public DialogWin { public: DlgHelp (HINSTANCE hInstance, HWND hParent, void *context); @@ -34,5 +35,16 @@ class DlgHelp: public DialogWin { int dlgw, dlgh; POINT pClose; }; +*/ -#endif // !__DLGHELP_H \ No newline at end of file +class DlgHelp: public ImGuiDialog +{ +public: + DlgHelp(); + void OnDraw() override; + void Display() override; + void OpenHelp(const HELPCONTEXT *hc); +// static void SetScenarioHelp (const char *_helpf); +// static void SetVesselHelp (const char *_helpf); +}; +#endif // !__DLGHELP_H diff --git a/Src/Orbiter/DlgInfo.cpp b/Src/Orbiter/DlgInfo.cpp index 87c574342..157bdb8c1 100644 --- a/Src/Orbiter/DlgInfo.cpp +++ b/Src/Orbiter/DlgInfo.cpp @@ -1,856 +1,675 @@ -// Copyright (c) Martin Schweiger +// Copyright (c) Martin Schweiger // Licensed under the MIT License // ====================================================================== // Object info window // ====================================================================== - -#define STRICT 1 -#define _WIN32_WINNT 0x0501 -#define OAPI_IMPLEMENTATION - -#include #include "DlgInfo.h" -#include "Dialogs.h" -#include "Resource.h" -#include "DlgMgr.h" // to be removed -#include "Orbiter.h" -#include "Vessel.h" -#include "Camera.h" -#include "Psys.h" #include "Celbody.h" -#include -#include -#include +#include "Psys.h" +#include "Astro.h" #include "Element.h" +#include "imgui.h" +#include "IconsFontAwesome6.h" -extern Orbiter *g_pOrbiter; -extern Vessel *g_focusobj; -extern Camera *g_camera; extern PlanetarySystem *g_psys; -extern TimeData td; -extern HELPCONTEXT DefHelpContext; -extern char DBG_MSG[256]; - -const char *na = "N/A"; - -// ====================================================================== - -DlgInfo::DlgInfo (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_OBJINFO, 0, 0, context) -{ - upd_t = 0.0; - upd_dt = 1.0; - body = NULL; - listmode = LIST_NONE; - pos = &g_pOrbiter->Cfg()->CfgWindowPos.DlgInfo; - - // note: shared icon resources should probably be loaded globally by orbiter - hIcon_dd = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_DDOWN)); - hIcon_du = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_DUP)); -} - -// ====================================================================== - -void DlgInfo::Init (HWND hDlg) -{ - RECT r; - POINT pt; - PARAFORMAT2 pfmt; - pfmt.cbSize = sizeof(PARAFORMAT2); - pfmt.dwMask = PFM_TABSTOPS; - pfmt.cTabCount = 1; - pfmt.rgxTabs[0] = 200; - int objtp = 0; - - SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Focus vessel"); - SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Camera target"); - SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Vessel"); - SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Base"); - SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_ADDSTRING, 0, (LPARAM)"Celestial body"); - if (body) { - switch (body->Type()) { - case OBJTP_VESSEL: objtp = 2; break; - case OBJTP_SURFBASE: objtp = 3; break; - case OBJTP_PLANET: - case OBJTP_STAR: objtp = 4; break; - } - } - SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_SETCURSEL, objtp, 0); - - SendDlgItemMessage (hDlg, IDC_INFO_DDN, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon_dd); - SendDlgItemMessage (hDlg, IDC_INFO_DUP, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon_du); - - int res = SendDlgItemMessage (hDlg, IDC_INFOBOX, EM_SETPARAFORMAT, 0, (LPARAM)&pfmt); - GetClientRect (hDlg, &r); - client_w = r.right; - client_h = r.bottom; - GetWindowRect (GetDlgItem (hDlg, IDC_INFOLIST), &r); - list_w = r.right-r.left; - list_h = r.bottom-r.top; - pt.x = r.left; - pt.y = r.top; - ScreenToClient (hDlg, &pt); - list_top = pt.y; - pl.OnInitDialog (hDlg, IDC_INFOLIST); - pl.SetColWidth (0, 180); - - Body *b = body; - if (!b) b = g_focusobj; - body = NULL; - - SetBody (hDlg, b); - BuildObjectList (hDlg, b); - if (objtp) { - int idx = SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_FINDSTRINGEXACT, 0, (LPARAM)body->Name()); - if (idx != CB_ERR) - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_SETCURSEL, idx, 0); - } -} - -// ====================================================================== - -void DlgInfo::SetBody (Body *bd) -{ - SetBody (hWnd, bd); - Init (hWnd); -} - -// ====================================================================== - -void DlgInfo::BuildObjectList (HWND hDlg, Body *b) -{ - Body *obj; - char cbuf[256]; - int j; - DWORD k; - int idx = SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_GETCURSEL, 0, 0); - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_RESETCONTENT, 0, 0); - - switch (idx) { - case 0: // focus vessel - strcpy (cbuf, g_focusobj->Name()); - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_ADDSTRING, 0, (LPARAM)cbuf); - break; - case 1: // camera target - strcpy (cbuf, g_camera->Target()->Name()); - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_ADDSTRING, 0, (LPARAM)cbuf); - break; - case 2: // vessels - for (k = 0; k < g_psys->nVessel(); k++) { - strcpy (cbuf, g_psys->GetVessel(k)->Name()); - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_ADDSTRING, 0, (LPARAM)cbuf); - } - break; - case 3: // spaceports - for (k = 0; k < g_psys->nGrav(); k++) { - obj = g_psys->GetGravObj (k); - if (obj->Type() != OBJTP_PLANET) continue; - Planet *planet = (Planet*)obj; - for (j = 0; j < g_psys->nBase(planet); j++) { - strcpy (cbuf, g_psys->GetBase (planet,j)->Name()); - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_ADDSTRING, 0, (LPARAM)cbuf); - } - } - break; - case 4: // celestial bodies - CBodySelectComboBox::BuildListFromNode (hDlg, IDC_INFO_NAME, (CelestialBody*)b); - return; - } - SendDlgItemMessage (hDlg, IDC_INFO_NAME, CB_SETCURSEL, 0, 0); - //Info_ShowData (hDlg); -} - -// ====================================================================== - -int DlgInfo::Size (DWORD width, DWORD height) -{ - int i, id; - RECT r; - POINT pt; - GetClientRect (hWnd, &r); - int dw = r.right-client_w; - int dh = r.bottom-client_h; - list_w += dw; - list_h += dh; - client_w = r.right; - client_h = r.bottom; - pl.Move (0, list_top, list_w, list_h); - - const int nbtl = 2; - const int btlid[nbtl] = {IDC_INFO_DDN, IDC_INFO_DUP}; - for (i = 0; i < nbtl; i++) { - id = btlid[i]; - GetWindowRect (GetDlgItem (hWnd, id), &r); - pt.x = r.left; - pt.y = r.top; - ScreenToClient (hWnd, &pt); - MoveWindow (GetDlgItem (hWnd, id), pt.x, pt.y+dh, r.right-r.left, r.bottom-r.top, TRUE); - } - const int nbtr = 3; - const int btrid[nbtr] = {IDC_INFO_MAP, IDCANCEL, IDHELP}; - for (i = 0; i < nbtr; i++) ShowWindow (GetDlgItem (hWnd, btrid[i]), SW_HIDE); - for (i = 0; i < nbtr; i++) { - id = btrid[i]; - GetWindowRect (GetDlgItem (hWnd, id), &r); - pt.x = r.left; - pt.y = r.top; - ScreenToClient (hWnd, &pt); - MoveWindow (GetDlgItem (hWnd, id), pt.x+dw, pt.y+dh, r.right-r.left, r.bottom-r.top, TRUE); - } - for (i = 0; i < nbtr; i++) ShowWindow (GetDlgItem (hWnd, btrid[i]), SW_SHOW); - return 0; -} - -// ====================================================================== - -void DlgInfo::OpenMap () -{ - if (body) { - DlgMap *pMap = g_pOrbiter->DlgMgr()->EnsureEntry (); - pMap->SetSelection (body); - } -} - -// ====================================================================== - -void DlgInfo::ExpandAll (HWND hDlg) -{ - pl.ExpandAll (true); -} - -// ====================================================================== -void DlgInfo::CollapseAll (HWND hDlg) -{ - pl.ExpandAll (false); +DlgInfo::DlgInfo() : ImGuiDialog(ICON_FA_CIRCLE_INFO " Orbiter: Object info", {753,423}) { + SetHelp("html/orbiter.chm", "/objinfo.htm"); } -// ====================================================================== - -void DlgInfo::Update () -{ - if (td.SysT0 > upd_t + upd_dt || td.SysT0 < upd_t) { - switch (listmode) { - case LIST_VESSEL: - UpdateItems_vessel (); - break; - case LIST_CBODY: - UpdateItems_celbody (); - break; - case LIST_BASE: - UpdateItems_base (); - break; - } - pl.Update (); - upd_t = td.SysT0; - } +void DlgInfo::AddCbodyNode(const CelestialBody *cbody) { + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth; + const bool is_selected = m_SelectedTarget == cbody->Name(); + if (is_selected) + node_flags |= ImGuiTreeNodeFlags_Selected; + if(cbody->nSecondary()) { + bool node_open = ImGui::TreeNodeEx(cbody->Name(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = cbody->Name(); + if(node_open) { + for (int i = 0; i < cbody->nSecondary(); i++) { + AddCbodyNode (cbody->Secondary(i)); + } + ImGui::TreePop(); + } + } else { + ImGui::TreeNodeEx(cbody->Name(), node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = cbody->Name(); + } } -// ====================================================================== - -void DlgInfo::SelectionChanged (HWND hDlg) -{ - char cbuf[256]; - int i, j; - - int tp = SendDlgItemMessage (hDlg, IDC_INFO_TYPE, CB_GETCURSEL, 0, 0); - GetWindowText (GetDlgItem (hDlg, IDC_INFO_NAME), cbuf, 256); - - if (tp == 1) { // camera target - Body *b = g_psys->GetObj (cbuf); - if (!b) b = g_psys->GetBase (cbuf); - if (!b) return; - switch (b->Type()) { - case OBJTP_VESSEL: - tp = 2; - break; - case OBJTP_SURFBASE: - tp = 3; - break; - case OBJTP_STAR: - case OBJTP_PLANET: - tp = 4; - break; - } - } - switch (tp) { - case 0: // focus vessel - SetBody (hDlg, g_focusobj); - break; - case 2: // vessel - SetBody (hDlg, g_psys->GetVessel (cbuf)); - break; - case 3: // surface base - for (i = 0; i < g_psys->nGrav(); i++) { - Body *obj; - obj = g_psys->GetGravObj (i); - if (obj->Type() != OBJTP_PLANET) continue; - Planet *planet = (Planet*)obj; - for (j = 0; j < g_psys->nBase(planet); j++) { - if (!_stricmp (g_psys->GetBase (planet,j)->Name(), cbuf)) { - SetBody (hDlg, g_psys->GetBase (planet,j)); - break; +void DlgInfo::DrawTree() { + for (int i = 0; i < g_psys->nStar(); i++) + AddCbodyNode (g_psys->GetStar(i)); + + if (ImGui::TreeNode("Spaceports")) { + for (int i = 0; i < g_psys->nGrav(); i++) { + Body *obj = g_psys->GetGravObj (i); + if (obj->Type() != OBJTP_PLANET) continue; + Planet *planet = (Planet*)obj; + if (g_psys->nBase(planet) > 0) { + if(ImGui::TreeNode(planet->Name())) { + for (int j = 0; j < g_psys->nBase(planet); j++) { + const char *name = g_psys->GetBase (planet,j)->Name(); + const bool is_selected = m_SelectedTarget == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name, node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = name; + } + ImGui::TreePop(); } - } - } - break; - case 4: { // celestial bodies - CelestialBody *cbody = CBodySelectComboBox::OnSelectionChanged (hDlg, IDC_INFO_NAME); - if (cbody) SetBody (hDlg, cbody); - } break; - } + } + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Vessels")) { + for (int i = 0; i < g_psys->nVessel(); i++) { + const char *name = g_psys->GetVessel(i)->Name(); + const bool is_selected = m_SelectedTarget == name; + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + if(is_selected) node_flags |= ImGuiTreeNodeFlags_Selected; + ImGui::TreeNodeEx(name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + m_SelectedTarget = name; + } + ImGui::TreePop(); + } } -// ====================================================================== - -void DlgInfo::SetBody (HWND hDlg, Body *bd) -{ - if (bd == body) return; // nothing to do - body = bd; - switch (bd->Type()) { - case OBJTP_VESSEL: - listmode = LIST_VESSEL; - break; - case OBJTP_STAR: - case OBJTP_PLANET: - listmode = LIST_CBODY; - break; - case OBJTP_SURFBASE: - listmode = LIST_BASE; - break; - } - switch (listmode) { - case LIST_VESSEL: - InitItems_vessel (hDlg, (Vessel*)body); - UpdateItems_vessel (); - break; - case LIST_CBODY: - InitItems_celbody (hDlg, (CelestialBody*)body); - UpdateItems_celbody (); - break; - case LIST_BASE: - InitItems_base (hDlg, (Base*)body); - UpdateItems_base (); - break; - } - pl.Redraw(); +void DlgInfo::DrawInfo() { + Vessel *vessel = g_psys->GetVessel(m_SelectedTarget.c_str(), true); + if(vessel) { + DrawInfoVessel(vessel); + return; + } + CelestialBody *cb = g_psys->GetGravObj(m_SelectedTarget.c_str(), true); + if(cb) { + DrawInfoCelestialBody(cb); + return; + } + Base *base = g_psys->GetBase(m_SelectedTarget.c_str(), true); + if(base) { + DrawInfoBase(base); + return; + } + ImGui::Text("Select an object on the left panel"); } -// ====================================================================== - -void DlgInfo::InitItems_vessel (HWND hDlg, const Vessel *vessel) -{ - const int nlabel_des = 3; - const char *label_des[nlabel_des] = { - "Name", - "Class", - "Transponder frequency" - }; - const int nlabel_prm = 5; - const char *label_prm[nlabel_prm] = { - "Total mass", - "Dry mass", - "Propellant mass", - "Mean radius", - "P. moments of inertia" - }; - const int nlabel_thr = 3; - const char *label_thr[nlabel_thr] = { - "Main", - "Retro", - "Hover" - }; - const int nlabel_els = 7; - const char *label_els[nlabel_els] = { - "Reference", - "Semi-major axis (a)", - "Eccentricity (e)", - "Inclination (i)", - "Longitude of asc. node", - "Longitude of periapsis", - "Mean longitude" - }; - const int nlabel_srf = 8; - const char *label_srf[nlabel_srf] = { - "Reference", - "Position", - "Altitude", - "Ground speed", - "Vertical speed", - "Heading", - "Pitch", - "Bank" - }; - const int nlabel_atm = 3; - const char *label_atm[nlabel_atm] = { - "Temperature", - "Density", - "Pressure" - }; - const int nlabel_aer = 8; - const char *label_aer[nlabel_aer] = { - "Dynamic pressure", - "True airspeed", - "Mach number", - "Lift", - "Drag", - "Weight", - "Lift/drag ratio", - "Angle of attack" - }; - const int nlabel_prp = 4; - const char *label_prp[nlabel_prp] = { - "Update mode", - "State propagator", - "Subsamples", - "Gravity sources" - }; - - int i; - PropertyItem *item; - pl.ClearGroups(); - - vlist.des = pl.AppendGroup(); - vlist.des->SetTitle ("Designation"); - for (i = 0; i < nlabel_des; i++) { - item = pl.AppendItem (vlist.des); - item->SetLabel (label_des[i]); - } - - vlist.prm = pl.AppendGroup(); - vlist.prm->SetTitle ("Physical parameters"); - for (i = 0; i < nlabel_prm; i++) { - item = pl.AppendItem (vlist.prm); - item->SetLabel (label_prm[i]); - } - - const THGROUP_TYPE thgrp[3] = {THGROUP_MAIN, THGROUP_RETRO, THGROUP_HOVER}; - bool showthgrp = false; - for (i = 0; i < 3; i++) { - showth[i] = (vessel->GetThrusterGroupMaxth (thgrp[i]) > 0.0); - if (showth[i]) showthgrp = true; - } - if (showthgrp) { - vlist.thr = pl.AppendGroup(); - vlist.thr->SetTitle ("Thruster group ratings (vacuum)"); - for (i = 0; i < nlabel_thr; i++) { - if (showth[i]) { - item = pl.AppendItem (vlist.thr); - item->SetLabel (label_thr[i]); - } - } - } else vlist.thr = NULL; - - vlist.els = pl.AppendGroup (); - vlist.els->SetTitle ("Osculating elements (ecliptic frame)"); - for (i = 0; i < nlabel_els; i++) { - item = pl.AppendItem (vlist.els); - item->SetLabel (label_els[i]); - } - - vlist.srf = pl.AppendGroup(); - vlist.srf->SetTitle ("Surface-relative parameters"); - for (i = 0; i < nlabel_srf; i++) { - item = pl.AppendItem (vlist.srf); - item->SetLabel (label_srf[i]); - } - - vlist.atm = pl.AppendGroup(); - vlist.atm->SetTitle ("Atmospheric parameters"); - for (i = 0; i < nlabel_atm; i++) { - item = pl.AppendItem (vlist.atm); - item->SetLabel (label_atm[i]); - } - - vlist.aer = pl.AppendGroup(); - vlist.aer->SetTitle ("Aerodynamic parameters"); - for (i = 0; i < nlabel_aer; i++) { - item = pl.AppendItem (vlist.aer); - item->SetLabel (label_aer[i]); - } - - if (vessel->nDock()) { - vlist.dck = pl.AppendGroup(); - vlist.dck->SetTitle ("Docking ports"); - char cbuf[64]; - for (i = 0; i < vessel->nDock(); i++) { - item = pl.AppendItem (vlist.dck); - sprintf (cbuf, "Port %d", i+1); - item->SetLabel (cbuf); - } - } else vlist.dck = NULL; - - vlist.prp = pl.AppendGroup(); - vlist.prp->SetTitle ("State propagation"); - for (i = 0; i < nlabel_prp; i++) { - item = pl.AppendItem (vlist.prp); - item->SetLabel (label_prp[i]); - } -} - -// ====================================================================== - -void DlgInfo::UpdateItems_vessel () -{ - const THGROUP_TYPE thgrp[3] = {THGROUP_MAIN, THGROUP_RETRO, THGROUP_HOVER}; - int i, j; - float f; - char cbuf[256]; - - Vessel *vessel = (Vessel*)body; - const Body *ref = vessel->ElRef(); - - vlist.des->GetItem (0)->SetValue (vessel->Name()); - vlist.des->GetItem (1)->SetValue (vessel->ClassName()); - if (vessel->GetXpdrFreq (f)) sprintf (cbuf, "%0.2fMHz", f); - else strcpy (cbuf, na); - vlist.des->GetItem (2)->SetValue (cbuf); - - sprintf (cbuf, "%g kg", vessel->Mass()); - vlist.prm->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%g kg", vessel->EmptyMass()); - vlist.prm->GetItem (1)->SetValue (cbuf); - sprintf (cbuf, "%g kg", vessel->FuelMass()); - vlist.prm->GetItem (2)->SetValue (cbuf); - strcpy (cbuf, DistStr (vessel->Size())+1); strcat (cbuf, "m"); - vlist.prm->GetItem (3)->SetValue (cbuf); - sprintf (cbuf, "(%4g, %4g, %4g) m²", vessel->PMI().x, vessel->PMI().y, vessel->PMI().z); - vlist.prm->GetItem (4)->SetValue (cbuf); - - if (vlist.thr) { - for (i = j = 0; i < 3; i++) { - if (showth[i]) { - double th = vessel->GetThrusterGroupMaxth (thgrp[i]); - if (th) { - sprintf (cbuf, "%sN", FloatStr (th)+1); - vlist.thr->GetItem (j)->SetValue (cbuf); - } else vlist.thr->GetItem (j)->SetValue (na); - j++; - } - } - } - - const Elements *el = vessel->Els(); - if (el && ref) { - vlist.els->GetItem (0)->SetValue (ref->Name()); - sprintf (cbuf, "%s m", SciStr (el->a, 5)); - vlist.els->GetItem (1)->SetValue (cbuf); - sprintf (cbuf, "%g", el->e); - vlist.els->GetItem (2)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->i*DEG); - vlist.els->GetItem (3)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->theta*DEG); - vlist.els->GetItem (4)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->omegab*DEG); - vlist.els->GetItem (5)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->MeanLng()*DEG); - vlist.els->GetItem (6)->SetValue (cbuf); - } else { - for (i = 0; i < 7; i++) - vlist.els->GetItem (i)->SetValue (na); - } - - const SurfParam *sp = vessel->GetSurfParam(); - if (sp) { - vlist.srf->GetItem (0)->SetValue (sp->ref->Name()); - sprintf (cbuf, "%07.3f°%c %06.3f°%c", fabs(sp->lng)*DEG, sp->lng >= 0.0 ? 'E':'W', fabs(sp->lat)*DEG, sp->lat >= 0.0 ? 'N':'S'); - vlist.srf->GetItem (1)->SetValue (cbuf); - strcpy (cbuf, DistStr (sp->alt)); strcat (cbuf, "m"); - vlist.srf->GetItem (2)->SetValue (cbuf+1); - sprintf (cbuf, "%sm/s", FloatStr (sp->groundspd)+1); - vlist.srf->GetItem (3)->SetValue (cbuf); - Vector V (mul (sp->L2H, tmul (vessel->ProxyBody()->GRot(), sp->groundvel_glob))); - sprintf (cbuf, "%sm/s", FloatStr (V.y)+1); - vlist.srf->GetItem (4)->SetValue (cbuf); - sprintf (cbuf, "%0.0f°", sp->dir*DEG); - vlist.srf->GetItem (5)->SetValue (cbuf); - sprintf (cbuf, "%0.0f°", sp->pitch*DEG); - vlist.srf->GetItem (6)->SetValue (cbuf); - sprintf (cbuf, "%0.0f° %s", fabs(sp->bank)*DEG, sp->bank >= 0.0 ? "left":"right"); - vlist.srf->GetItem (7)->SetValue (cbuf); - } else { - for (i = 0; i < 8; i++) - vlist.srf->GetItem (i)->SetValue (na); - } - - if (vessel->isInAtmosphere()) { - double T, rho, p; - vessel->AtmTemperature (T); - vessel->AtmPressureAndDensity (p, rho); - sprintf (cbuf, "%0.2f K", T); - vlist.atm->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%0.4g kg/m^3", rho); - vlist.atm->GetItem (1)->SetValue (cbuf); - sprintf (cbuf, "%sPa", FloatStr(p)+1); - vlist.atm->GetItem (2)->SetValue (cbuf); - } else { - for (i = 0; i < 3; i++) - vlist.atm->GetItem (i)->SetValue (na); - } - - if (vessel->isInAtmosphere()) { - double dynp, M, L, D; - vessel->DynPressure (dynp); - L = vessel->GetLift(); - D = vessel->GetDrag(); - sprintf (cbuf, "%sPa", FloatStr(dynp)+1); - vlist.aer->GetItem (0)->SetValue (cbuf); - if (sp) sprintf (cbuf, "%sm/s", FloatStr(sp->airspd)+1); - else strcpy (cbuf, na); - vlist.aer->GetItem (1)->SetValue (cbuf); - vessel->MachNumber (M); - sprintf (cbuf, "%g", M); - vlist.aer->GetItem (2)->SetValue (cbuf); - sprintf (cbuf, "%sN", FloatStr(L)+(L >= 0 ? 1:0)); - vlist.aer->GetItem (3)->SetValue (cbuf); - sprintf (cbuf, "%sN", FloatStr(D)+1); - vlist.aer->GetItem (4)->SetValue (cbuf); - sprintf (cbuf, "%sN", FloatStr(vessel->GetWeight())+1); - vlist.aer->GetItem (5)->SetValue (cbuf); - if (D) sprintf (cbuf, "%g", L/D); - else strcpy (cbuf, na); - vlist.aer->GetItem (6)->SetValue (cbuf); - if (sp) sprintf (cbuf, "%+0.1f°", -atan2 (sp->groundvel_ship.y, sp->groundvel_ship.z)*DEG); - else strcpy (cbuf, na); - vlist.aer->GetItem (7)->SetValue (cbuf); - } else { - for (i = 0; i < 8; i++) - vlist.aer->GetItem (i)->SetValue (na); - } - - if (vlist.dck) { - for (i = 0; i < vlist.dck->ItemCount(); i++) { - if (i < vessel->nDock()) { - if (vessel->GetDockParams(i)->ids) - sprintf (cbuf, "IDS %06.2f ", vessel->GetDockParams(i)->ids->GetFreq()); - else cbuf[0] = '\0'; - Vessel *mate = vessel->DockMate (i); - if (mate) sprintf (cbuf+strlen(cbuf), "[Docked to %s]", mate->Name()); - else strcat (cbuf, "[free]"); - vlist.dck->GetItem (i)->SetValue (cbuf); - } else vlist.dck->GetItem (i)->SetValue (na); - } - } - if (vessel->GetStatus() == FLIGHTSTATUS_LANDED) { - vlist.prp->GetItem (0)->SetValue ("IDLE (landed)"); - for (i = 1; i < 4; i++) vlist.prp->GetItem (i)->SetValue (na); - } else if (vessel->isAttached()) { - vlist.prp->GetItem (0)->SetValue ("PASSIVE (attached)"); - for (i = 1; i < 4; i++) vlist.prp->GetItem (i)->SetValue (na); - } else { - vlist.prp->GetItem (0)->SetValue ("ACTIVE (dynamic)"); - vlist.prp->GetItem (1)->SetValue (vessel->isOrbitStabilised() ? "stabilised" : vessel->CurPropagatorStr()); - sprintf (cbuf, "%d", vessel->CurPropagatorSubsamples()); - vlist.prp->GetItem (2)->SetValue (cbuf); - cbuf[0] = '\0'; - const GFieldData &gfd = vessel->GetGFieldData(); - for (i = 0; i < gfd.ngrav; i++) { - if (i) strcat (cbuf, ", "); - strcat (cbuf, g_psys->GetGravObj(gfd.gravidx[i])->Name()); - } - vlist.prp->GetItem (3)->SetValue (cbuf); - } -} - -// ====================================================================== - -void DlgInfo::InitItems_celbody (HWND hDlg, const CelestialBody *cbody) -{ - const int nlabel_des = 3; - const char *label_des[nlabel_des] = { - "Name", - "Primary", - "Solar system" - }; - const int nlabel_prm = 7; - const char *label_prm[nlabel_prm] = { - "Mass", - "Mean radius", - "Gravitational moments", - "Siderial day", - "Orbit period", - "Obliquity of ecliptic", - "Atmosphere" - }; - const int nlabel_atm = 5; - const char *label_atm[nlabel_atm] = { - "Atmosphere model", - "Surface pressure", - "Surface density", - "Specific gas constant", - "Specific heat ratio" - }; - const int nlabel_els = 6; - const char *label_els[nlabel_els] = { - "Semi-major axis (a)", - "Eccentricity (e)", - "Inclination (i)", - "Longitude of asc. node", - "Longitude of periapsis", - "Mean longitude" - }; - const int nlabel_loc = 2; - const char *label_loc[nlabel_loc] = { - "Right ascension (RA)", - "Declination (Dec)" - }; - const int nlabel_ecl = 3; - const char *label_ecl[nlabel_ecl] = { - "Longitude", - "Latitude", - "Radial distance" - }; - const int nlabel_prp = 2; - const char *label_prp[nlabel_prp] = { - "Mode", - "Gravity sources" - }; - - int i; - PropertyItem *item; - pl.ClearGroups(); - - cblist.des = pl.AppendGroup(); - cblist.des->SetTitle ("Designation"); - for (i = 0; i < nlabel_des; i++) { - item = pl.AppendItem (cblist.des); - item->SetLabel (label_des[i]); - } - cblist.prm = pl.AppendGroup(); - cblist.prm->SetTitle ("Physical parameters"); - for (i = 0; i < nlabel_prm; i++) { - item = pl.AppendItem (cblist.prm); - item->SetLabel (label_prm[i]); - } - if (cbody->Type() == OBJTP_PLANET && ((Planet*)cbody)->AtmParams()) { - cblist.atm = pl.AppendGroup(); - cblist.atm->SetTitle ("Atmosphere"); - for (i = 0; i < nlabel_atm; i++) { - item = pl.AppendItem (cblist.atm); - item->SetLabel (label_atm[i]); - } - } else cblist.atm = NULL; - cblist.els = pl.AppendGroup(); - cblist.els->SetTitle ("Osculating elements (ecliptic frame)"); - for (i = 0; i < nlabel_els; i++) { - item = pl.AppendItem (cblist.els); - item->SetLabel (label_els[i]); - } - if (strcmp (cbody->Name(), "Earth")) { - cblist.loc = pl.AppendGroup(); - cblist.loc->SetTitle ("Geocentric celestial position"); - for (i = 0; i < nlabel_loc; i++) { - item = pl.AppendItem (cblist.loc); - item->SetLabel (label_loc[i]); - } - } else cblist.loc = NULL; - if (cbody->Els()) { - cblist.ecl = pl.AppendGroup(); - cblist.ecl->SetTitle ("Ecliptic position from primary"); - for (i = 0; i < nlabel_ecl; i++) { - item = pl.AppendItem (cblist.ecl); - item->SetLabel (label_ecl[i]); - } - } else cblist.ecl = NULL; - cblist.prp = pl.AppendGroup(); - cblist.prp->SetTitle ("State propagation"); - for (i = 0; i < nlabel_prp - (cbody->canDynamicPosVel() ? 0:1); i++) { - item = pl.AppendItem (cblist.prp); - item->SetLabel (label_prp[i]); - } +void DlgInfo::DrawInfoVessel(Vessel *vessel) { + ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; + if(ImGui::CollapsingHeader("Designation", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table Designation", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Name"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(vessel->Name()); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Class"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(vessel->ClassName()); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Transponder Frequency"); + ImGui::TableSetColumnIndex(1); + float f; + if (vessel->GetXpdrFreq (f)) + ImGui::Text("%0.2fMHz", f); + else + ImGui::TextUnformatted("N/A"); + ImGui::EndTable(); + } + } + if(ImGui::CollapsingHeader("Physical Parameters", ImGuiTreeNodeFlags_DefaultOpen)) { + // Total mass, dry mass, propellant mass, mean radius, P. moment of inertias + if (ImGui::BeginTable("table Physical Parameters", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Total mass"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%g kg", vessel->Mass()); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Dry mass"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%g kg", vessel->EmptyMass()); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Propellant mass"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%g kg", vessel->FuelMass()); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mean radius"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s m", DistStr (vessel->Size())+1); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("P. moments of inertia"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"(%4g, %4g, %4g) kg.m²", vessel->PMI().x, vessel->PMI().y, vessel->PMI().z); + + ImGui::EndTable(); + } + } + if(ImGui::CollapsingHeader("Thrusters Group Ratings (vacuum)", ImGuiTreeNodeFlags_DefaultOpen)) { + //Main, retro, hover + const THGROUP_TYPE thgrp[3] = {THGROUP_MAIN, THGROUP_RETRO, THGROUP_HOVER}; + const char *thrtype[] = {"Main", "Retro", "Hover"}; + if (ImGui::BeginTable("table Thrusters Group Ratings", 2, flags)) + { + for (int i = 0; i < 3; i++) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(thrtype[i]); + ImGui::TableSetColumnIndex(1); + double th = vessel->GetThrusterGroupMaxth (thgrp[i]); + if (th > 0.0) { + ImGui::Text("%sN", FloatStr (th)+1); + } else { + ImGui::Text("N/A"); + } + } + ImGui::EndTable(); + } + + } + + const Body *ref = vessel->ElRef(); + const Elements *el = vessel->Els(); + if(el && ref && ImGui::CollapsingHeader("Osculating Elements (Ecliptic Frame)", ImGuiTreeNodeFlags_DefaultOpen)) { + //Reference, semi major axis, excentricity, inclination, longitude of AN, longitude of periapsis, mean longitude + if (ImGui::BeginTable("table Osculating Elements", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Reference"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(ref->Name()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Semi-major Axis (a)"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s m", SciStr (el->a, 5)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Excentricity (e)"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%g", el->e); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(u8"Inclination (igg)"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->i*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Longitude of Ascending Node"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->theta*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Longitude of Periapsis"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->omegab*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mean longitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->MeanLng()*DEG); + ImGui::EndTable(); + } + } + + const SurfParam *sp = vessel->GetSurfParam(); + if(sp && ImGui::CollapsingHeader("Surface-relative Parameters", ImGuiTreeNodeFlags_DefaultOpen)) { + //reference, position, altitude, ground speed, vertical speed, heading, pitch, bank + if (ImGui::BeginTable("table Surface-relative Parameters", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Reference"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(sp->ref->Name()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Position"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%07.3f°%c %06.3f°%c", fabs(sp->lng)*DEG, sp->lng >= 0.0 ? 'E':'W', fabs(sp->lat)*DEG, sp->lat >= 0.0 ? 'N':'S'); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Altitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sm", DistStr (sp->alt)+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Ground Speed"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sm/s", FloatStr (sp->groundspd)+1); + + Vector V (mul (sp->L2H, tmul (vessel->ProxyBody()->GRot(), sp->groundvel_glob))); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Vertical Speed"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sm/s", FloatStr (V.y)+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Heading"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.0f°", sp->dir*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Pitch"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.0f°", sp->pitch*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Bank"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.0f° %s", fabs(sp->bank)*DEG, sp->bank >= 0.0 ? "left":"right"); + + ImGui::EndTable(); + } + } + + if (vessel->isInAtmosphere()) { + if(ImGui::CollapsingHeader("Atmospheric Parameters", ImGuiTreeNodeFlags_DefaultOpen)) { + // Temperature, density, pressure + if (ImGui::BeginTable("table Atmospheric Parameters", 2, flags)) + { + double T, rho, p; + vessel->AtmTemperature (T); + vessel->AtmPressureAndDensity (p, rho); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Temperature"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%0.2f K", T); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Density"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%0.4g kg/m^3", rho); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Pressure"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sPa", FloatStr(p)+1); + + ImGui::EndTable(); + } + } + if(ImGui::CollapsingHeader("Aerodynamic Parameters", ImGuiTreeNodeFlags_DefaultOpen)) { + // Dynamic pressure, true airspeed, mach number, lift, drag, weight, lift/drag ratio, angle of attack + if (ImGui::BeginTable("table Aerodynamic Parameters", 2, flags)) + { + double dynp, M, L, D; + vessel->DynPressure (dynp); + L = vessel->GetLift(); + D = vessel->GetDrag(); + vessel->MachNumber (M); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Dynamic Pressure"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sPa", FloatStr(dynp)+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("True Airspeed"); + ImGui::TableSetColumnIndex(1); + ImGui::Text( "%sm/s", FloatStr(sp->airspd)+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mach Number"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%g", M); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Lift"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sN", FloatStr(L)+(L >= 0 ? 1:0)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Drag"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sN", FloatStr(D)+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Weight"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sN", FloatStr(vessel->GetWeight())+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Lift/Drag Ratio"); + ImGui::TableSetColumnIndex(1); + if (D) + ImGui::Text("%g", L/D); + else + ImGui::TextUnformatted("N/A"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Angle of Attack"); + ImGui::TableSetColumnIndex(1); + if(sp) + ImGui::Text(u8"%+0.1f°", -atan2 (sp->groundvel_ship.y, sp->groundvel_ship.z)*DEG); + else + ImGui::TextUnformatted("N/A"); + + ImGui::EndTable(); + } + } + } + + if (vessel->nDock()) { + if(ImGui::CollapsingHeader("Docking Ports", ImGuiTreeNodeFlags_DefaultOpen)) { + // port 1,2,3... + if (ImGui::BeginTable("table Docking Ports", 2, flags)) + { + for(int i = 0;inDock();i++) { + char buf[128]; + if (vessel->GetDockParams(i)->ids) + sprintf (buf, "IDS %06.2f ", vessel->GetDockParams(i)->ids->GetFreq()); + else + buf[0]='\0'; + Vessel *mate = vessel->DockMate (i); + if (mate) sprintf (buf+strlen(buf), "[Docked to %s]", mate->Name()); + else strcat (buf, "[free]"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Port %d", i+1); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(buf); + } + + ImGui::EndTable(); + } + } + } + + if(ImGui::CollapsingHeader("State Propagation", ImGuiTreeNodeFlags_DefaultOpen)) { + // update mode, state propagator, subsamples, gravity sources + if (ImGui::BeginTable("table State Propagation", 2, flags)) + { + if (vessel->GetStatus() == FLIGHTSTATUS_LANDED) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Update mode"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("IDLE (landed)"); + } else if (vessel->isAttached()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Update mode"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("PASSIVE (attached)"); + } else { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Update mode"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("ACTIVE (dynamic)"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("State Propagator"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(vessel->isOrbitStabilised() ? "stabilised" : vessel->CurPropagatorStr()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Subsamples"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%d", vessel->CurPropagatorSubsamples()); + + char cbuf[256]; + cbuf[0] = '\0'; + const GFieldData &gfd = vessel->GetGFieldData(); + for (int i = 0; i < gfd.ngrav; i++) { + if (i) strcat (cbuf, ", "); + strcat (cbuf, g_psys->GetGravObj(gfd.gravidx[i])->Name()); + } + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Gravity Sources"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + + } + ImGui::EndTable(); + } + } } -// ====================================================================== -void DlgInfo::UpdateItems_celbody () -{ - CelestialBody *cbody = (CelestialBody*)body; - CELBODY *cb = cbody->GetModuleInterface(); - Planet *planet = 0; - const Elements *el = 0; - char cbuf[256]; - int i, nj; - - switch (body->Type()) { - case OBJTP_PLANET: - planet = (Planet*)body; - el = planet->Els(); - break; - } - - cblist.des->GetItem (0)->SetValue (cbody->Name()); - cblist.des->GetItem (1)->SetValue (cbody->ElRef() ? cbody->ElRef()->Name() : na); - const std::string& psys_name = g_psys->Name(); - cblist.des->GetItem (2)->SetValue (psys_name.empty() ? na : psys_name.c_str()); - - sprintf (cbuf, "%s kg", SciStr (cbody->Mass(), 4)); - cblist.prm->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%s m", SciStr (cbody->Size(), 4)); - cblist.prm->GetItem (1)->SetValue (cbuf); - if (nj = cbody->nJcoeff()) { - cbuf[0] = '\0'; - for (i = 0; i < nj; i++) { - sprintf (cbuf + strlen(cbuf), "J%d=%s", i+2, SciStr (cbody->Jcoeff (i),3)); - if (i < nj-1) strcat (cbuf, ", "); - } - } else strcpy (cbuf, na); - cblist.prm->GetItem (2)->SetValue (cbuf); - sprintf (cbuf, "%s s", SciStr (cbody->RotT(), 4)); - cblist.prm->GetItem (3)->SetValue (cbuf); - if (el) sprintf (cbuf, "%s s", SciStr (el->OrbitT(), 4)); - else strcpy (cbuf, na); - cblist.prm->GetItem (4)->SetValue (cbuf); - if (planet) sprintf (cbuf, "%0.2f°", planet->Obliquity()*DEG); - else strcpy (cbuf, na); - cblist.prm->GetItem (5)->SetValue (cbuf); - cblist.prm->GetItem (6)->SetValue (planet && cblist.atm ? "Yes":"No"); - - if (planet && cblist.atm) { - const ATMCONST *ap = planet->AtmParams(); - if (ap) { - strcpy (cbuf, "Generic"); - if (cb && cb->Version() >= 2) { - CELBODY2 *cb2 = (CELBODY2*)cb; - ATMOSPHERE *atm = cb2->GetAtmosphere(); - if (atm) strcpy (cbuf, atm->clbkName()); - } - cblist.atm->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%sPa", FloatStr (ap->p0)+1); - cblist.atm->GetItem (1)->SetValue (cbuf); - sprintf (cbuf, "%skg/m^3", SciStr (ap->rho0)); - cblist.atm->GetItem (2)->SetValue (cbuf); - sprintf (cbuf, "%0.2fJ/(K kg)", ap->R); - cblist.atm->GetItem (3)->SetValue (cbuf); - sprintf (cbuf, "%0.2f", ap->gamma); - cblist.atm->GetItem (4)->SetValue (cbuf); - } else { - for (i = 0; i < 5; i++) - cblist.atm->GetItem (i)->SetValue (na); - } - } - - if (el) { - sprintf (cbuf, "%s m", SciStr (el->a, 5)); - cblist.els->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%0.5g", el->e); - cblist.els->GetItem (1)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->i*DEG); - cblist.els->GetItem (2)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->theta*DEG); - cblist.els->GetItem (3)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->omegab*DEG); - cblist.els->GetItem (4)->SetValue (cbuf); - sprintf (cbuf, "%0.2f°", el->MeanLng()*DEG); - cblist.els->GetItem (5)->SetValue (cbuf); - } else { - for (i = 0; i < 6; i++) - cblist.els->GetItem (i)->SetValue (na); - } - - if (cblist.loc) { - Planet *earth = g_psys->GetPlanet ("Earth"); - if (earth && earth != cbody) { +void DlgInfo::DrawInfoCelestialBody(CelestialBody *cbody) { + const Elements *el = nullptr; + const Planet *planet = nullptr; + const CELBODY *cb = cbody->GetModuleInterface(); + + switch (cbody->Type()) { + case OBJTP_PLANET: + planet = (Planet*)cbody; + el = planet->Els(); + break; + } + + ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; + if(ImGui::CollapsingHeader("Designation", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody designation", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Name"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbody->Name()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Primary"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbody->ElRef() ? cbody->ElRef()->Name() : "N/A"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Solar System"); + ImGui::TableSetColumnIndex(1); + const char *psys_name = g_psys->Name().c_str(); + ImGui::TextUnformatted(psys_name ? psys_name : "N/A"); + + ImGui::EndTable(); + } + } + if(ImGui::CollapsingHeader("Physical Parameters", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody Physical parameters", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mass"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s kg", SciStr (cbody->Mass(), 4)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mean Radius"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s m", SciStr (cbody->Size(), 4)); + + int nj; + char cbuf[128]; + if ((nj = cbody->nJcoeff())) { + cbuf[0] = '\0'; + for (int i = 0; i < nj; i++) { + sprintf (cbuf + strlen(cbuf), "J%d=%s", i+2, SciStr (cbody->Jcoeff (i),3)); + if (i < nj-1) strcat (cbuf, ", "); + } + } else strcpy (cbuf, "N/A"); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Gravitational Moments"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Siderial day"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s s", SciStr (cbody->RotT(), 4)); + + if (el) sprintf (cbuf, "%s s", SciStr (el->OrbitT(), 4)); + else strcpy (cbuf, "N/A"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Orbit Period"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + + if (planet) sprintf (cbuf, u8"%0.2f°", planet->Obliquity()*DEG); + else strcpy (cbuf, "N/A"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Obliquity of Ecliptic"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Atmosphere"); + ImGui::TableSetColumnIndex(1); + if(planet && planet->AtmParams()) { + ImGui::TextUnformatted("Yes"); + } else { + ImGui::TextUnformatted("No"); + } + + + ImGui::EndTable(); + } + } + if(planet && planet->AtmParams() && ImGui::CollapsingHeader("Atmosphere", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody Atmosphere", 2, flags)) + { + char cbuf[128]; + const ATMCONST *ap = planet->AtmParams(); + strcpy (cbuf, "Generic"); + if (cb && cb->Version() >= 2) { + CELBODY2 *cb2 = (CELBODY2*)cb; + ATMOSPHERE *atm = cb2->GetAtmosphere(); + if (atm) strcpy (cbuf, atm->clbkName()); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Atmosphere Model"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Surface Pressure"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%sPa", FloatStr (ap->p0)+1); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Surface density"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%skg/m^3", SciStr (ap->rho0)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Specific Gas Constant"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%0.2fJ/(K kg)", ap->R); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Specific Heat Ratio"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%0.2f", ap->gamma); + + ImGui::EndTable(); + } + } + + if(el && ImGui::CollapsingHeader("Osculating Elements (Ecliptic Frame)", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody Osculating elements (ecliptic frame)", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Semi-major Axis (a)"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s m", SciStr (el->a, 5)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Eccentricity (e)"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%0.5g", el->e); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Inclination (i)"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->i*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Longitude of Ascending Node"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->theta*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Longitude of Periapsis"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->omegab*DEG); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mean Longitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.2f°", el->MeanLng()*DEG); + + ImGui::EndTable(); + } + } + if(strcmp(cbody->Name(), "Earth") && ImGui::CollapsingHeader("Geocentric Celestial Position", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody Geocentric Celestial Position", 2, flags)) + { + char cbuf[128]; + Planet *earth = g_psys->GetPlanet ("Earth"); Vector p (cbody->GPos() - earth->GPos()); double r = p.length(); double lng = atan2 (p.z, p.x); @@ -863,225 +682,221 @@ void DlgInfo::UpdateItems_celbody () ras = modf (ram, &ram) * 60.0; dcm = fabs (modf (dc*DEG, &dcd)) * 60.0; dcs = modf (dcm, &dcm) * 60.0; - sprintf (cbuf, "%02.0fh %02.0fm %02.2fs", rah, ram, ras); - cblist.loc->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%+02.0f° %02.0f' %02.2f''", dcd, dcm, dcs); - cblist.loc->GetItem (1)->SetValue (cbuf); - } else { - for (i = 0; i < 2; i++) - cblist.loc->GetItem (i)->SetValue (na); - } - } - - if (cblist.ecl) { - if (el) { + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Right Ascension (RA)"); + ImGui::TableSetColumnIndex(1); + sprintf (cbuf, "%02.0fh %02.0fm %02.2fs", rah, ram, ras); + ImGui::TextUnformatted(cbuf); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Declination (Dec)"); + ImGui::TableSetColumnIndex(1); + sprintf (cbuf, u8"%+02.0f° %02.0f' %02.2f''", dcd, dcm, dcs); + ImGui::TextUnformatted(cbuf); + + ImGui::EndTable(); + } + } + + if(el && ImGui::CollapsingHeader("Ecliptic position from primary", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody Ecliptic position from primary", 2, flags)) + { Vector p (cbody->GPos() - cbody->ElRef()->GPos()); double r = p.length(); double lng = atan2 (p.z, p.x); double lat = p.y/r; - sprintf (cbuf, "%0.3f°", DEG*posangle(lng)); - cblist.ecl->GetItem (0)->SetValue (cbuf); - sprintf (cbuf, "%0.3f°", DEG*lat); - cblist.ecl->GetItem (1)->SetValue (cbuf); - sprintf (cbuf, "%s m", SciStr (r)); - cblist.ecl->GetItem (2)->SetValue (cbuf); - } else { - for (i = 0; i < 3; i++) - cblist.ecl->GetItem (i)->SetValue (na); - } - } - - if (cbody->canDynamicPosVel()) { - cblist.prp->GetItem (0)->SetValue ("Numerical"); - if (cblist.prp->ItemCount() > 1) { - const GFieldData &gfd = cbody->GetGFieldData(); - if (gfd.ngrav) { - cbuf[0] = '\0'; - for (i = 0; i < gfd.ngrav; i++) { - strcat (cbuf, g_psys->GetGravObj(gfd.gravidx[i])->Name()); - if (i < gfd.ngrav-1) strcat (cbuf, ", "); - } - cblist.prp->GetItem (1)->SetValue (cbuf); - } else { - cblist.prp->GetItem (1)->SetValue (na); - } - } - } else { - sprintf (cbuf, "Analytic (%s)", cb ? "from module" : "2-body"); - cblist.prp->GetItem (0)->SetValue (cbuf); - } -} -// ====================================================================== + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Longitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.3f°", DEG*posangle(lng)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Latitude"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%0.3f°", DEG*lat); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Radial Distance"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s m", SciStr (r)); + + ImGui::EndTable(); + } + } + if(ImGui::CollapsingHeader("State Propagation", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table celbody State propagation", 2, flags)) + { + if (cbody->canDynamicPosVel()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mode"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("Numerical"); + + const GFieldData &gfd = cbody->GetGFieldData(); + if (gfd.ngrav) { + char cbuf[128]; + cbuf[0] = '\0'; + for (int i = 0; i < gfd.ngrav; i++) { + strcat (cbuf, g_psys->GetGravObj(gfd.gravidx[i])->Name()); + if (i < gfd.ngrav-1) strcat (cbuf, ", "); + } + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Gravity Sources"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + + } + } else { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Mode"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("Analytic (%s)", cb ? "from module" : "2-body"); + } + + ImGui::EndTable(); + } + } -void DlgInfo::InitItems_base (HWND hDlg, const Base *base) -{ - const int nlabel_des = 3; - const char *label_des[nlabel_des] = { - "Name", - "Located on", - "Position" - }; - - int i; - DWORD j; - PropertyItem *item; - pl.ClearGroups(); - blist.des = pl.AppendGroup(); - blist.des->SetTitle ("Designation"); - for (i = 0; i < nlabel_des; i++) { - item = pl.AppendItem (blist.des); - item->SetLabel (label_des[i]); - } - if (base->nPad()) { - char cbuf[64]; - blist.pad = pl.AppendGroup(); - blist.pad->SetTitle ("Landing pads"); - for (i = 0; i < base->nPad(); i++) { - item = pl.AppendItem (blist.pad); - sprintf (cbuf, "Pad %d", i+1); - item->SetLabel (cbuf); - } - } else blist.pad = NULL; - - if (base->nRwy()) { - char cbuf[64]; - blist.rwy = pl.AppendGroup(); - blist.rwy->SetTitle ("Runways"); - for (j = 0; j < base->nRwy(); j++) { - item = pl.AppendItem (blist.rwy); - const RwySpec *rwy = base->RwyStatus (j); - int dir = (int)(posangle(rwy->appr1)*DEG*0.1+0.5); - sprintf (cbuf, "Runway %02d/%02d", dir, (dir+18)%36); - item->SetLabel (cbuf); - } - } else blist.rwy = NULL; - - if (base->nVOR()) { - for (j = 0; j < base->nVOR(); j++) - if (base->VOR(j)->Type() == TRANSMITTER_VOR) - break; - if (j < base->nVOR()) { - blist.vor = pl.AppendGroup(); - blist.vor->SetTitle ("VOR transmitters"); - for (j = 0; j < base->nVOR(); j++) { - const Nav *nav = base->VOR(j); - if (nav->Type() == TRANSMITTER_VOR) { - item = pl.AppendItem (blist.vor); - item->SetLabel (nav->GetId()); - } - } - } - } else blist.vor = NULL; } - -// ====================================================================== - -void DlgInfo::UpdateItems_base () -{ - const char *c, *statusstr[3] = {"free", "", "reserved"}; - char cbuf[256]; - double lng, lat; - int i, j, status; - - Base *base = (Base*)body; - blist.des->GetItem (0)->SetValue (base->Name()); - blist.des->GetItem (1)->SetValue (base->RefPlanet()->Name()); - base->EquPos (lng, lat); - sprintf (cbuf, "%07.3f°%c %06.3f°%c", - fabs(lng)*DEG, lng >= 0.0 ? 'E':'W', - fabs(lat)*DEG, lat >= 0.0 ? 'N':'S' - ); - blist.des->GetItem (2)->SetValue (cbuf); - - if (blist.pad) { - for (i = 0; i < blist.pad->ItemCount(); i++) { - if (i < base->nPad()) { - cbuf[0] = '\0'; - status = base->PadStatus(i)->status; - if (status == 1) c = base->PadStatus(i)->vessel->Name(); - else c = statusstr[status]; - if (base->PadStatus(i)->nav) - sprintf (cbuf, "ILS %06.2f ", base->PadStatus(i)->nav->GetFreq()); - sprintf (cbuf+strlen (cbuf), "[%s]", c); - blist.pad->GetItem(i)->SetValue(cbuf); - } else blist.pad->GetItem(i)->SetValue(na); - } - } - - if (blist.rwy) { - for (i = 0; i < blist.rwy->ItemCount(); i++) { - if (i < base->nRwy()) { - const RwySpec *rwy = base->RwyStatus (i); - char ils1[20], ils2[20]; - if (rwy->ils1) sprintf (ils1, "%06.2f", rwy->ils1->GetFreq()); - else strcpy (ils1, "--"); - if (rwy->ils2) sprintf (ils2, "%06.2f", rwy->ils2->GetFreq()); - else strcpy (ils2, "--"); - sprintf (cbuf, "ILS %s/%s, length %0.0fm", ils1, ils2, rwy->length); - blist.rwy->GetItem(i)->SetValue(cbuf); - } else blist.rwy->GetItem(i)->SetValue(na); - } - } - - if (blist.vor) { - for (i = j = 0; i < base->nVOR(); i++) { - const Nav *nav = base->VOR(i); - if (nav->Type() == TRANSMITTER_VOR && j < blist.vor->ItemCount()) { - sprintf (cbuf, "%06.2f, range %sm", nav->GetFreq(), DistStr (nav->GetRange())); - blist.vor->GetItem(j++)->SetValue(cbuf); - } - } - } +void DlgInfo::DrawInfoBase(Base *base) { + ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; + if(ImGui::CollapsingHeader("Designation", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table base designation", 2, flags)) + { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Name"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(base->Name()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Located on"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(base->RefPlanet()->Name()); + + double lng, lat; + base->EquPos (lng, lat); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Position"); + ImGui::TableSetColumnIndex(1); + ImGui::Text(u8"%07.3f°%c %06.3f°%c",fabs(lng)*DEG, lng >= 0.0 ? 'E':'W',fabs(lat)*DEG, lat >= 0.0 ? 'N':'S'); + + ImGui::EndTable(); + } + } + + // Landing pads + // pad 1 + // pad xxx + if(base->nPad()) { + if(ImGui::CollapsingHeader("Landing Pads", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table Landing Pads", 2, flags)) + { + const char *c, *statusstr[3] = {"free", "", "reserved"}; + for(int i=0;inPad();i++) { + char cbuf[256]; + cbuf[0] = '\0'; + int status = base->PadStatus(i)->status; + if (status == 1) c = base->PadStatus(i)->vessel->Name(); + else c = statusstr[status]; + if (base->PadStatus(i)->nav) + sprintf (cbuf, "ILS %06.2f ", base->PadStatus(i)->nav->GetFreq()); + sprintf (cbuf+strlen (cbuf), "[%s]", c); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Pad %d", i+1); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + } + ImGui::EndTable(); + } + } + } + + // Runways + if(base->nRwy()) { + if(ImGui::CollapsingHeader("Runways", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table Runways", 2, flags)) + { + for(int i=0;inRwy();i++) { + char cbuf[256]; + const RwySpec *rwy = base->RwyStatus (i); + int dir = (int)(posangle(rwy->appr1)*DEG*0.1+0.5); + char ils1[20], ils2[20]; + if (rwy->ils1) sprintf (ils1, "%06.2f", rwy->ils1->GetFreq()); + else strcpy (ils1, "--"); + if (rwy->ils2) sprintf (ils2, "%06.2f", rwy->ils2->GetFreq()); + else strcpy (ils2, "--"); + sprintf (cbuf, "ILS %s/%s, length %0.0fm", ils1, ils2, rwy->length); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("Runway %02d/%02d", dir, (dir+18)%36); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted(cbuf); + } + ImGui::EndTable(); + } + } + } + + // VOR + if(base->nVOR()) { + if(ImGui::CollapsingHeader("VOR Transmitters", ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("table VOR", 2, flags)) + { + for(int i=0;inVOR();i++) { + const Nav *nav = base->VOR(i); + if(nav->Type() == TRANSMITTER_VOR) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(nav->GetId()); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%06.2f, range %sm", nav->GetFreq(), DistStr (nav->GetRange())); + } + } + ImGui::EndTable(); + } + } + } } -// ====================================================================== - -BOOL DlgInfo::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - Init (hDlg); - return TRUE; -} - -// ====================================================================== - -BOOL DlgInfo::OnSize (HWND hDlg, WPARAM wParam, int w, int h) -{ - Size (w, h); - return DialogWin::OnSize (hDlg, wParam, w, h); // allow default processing +void DlgInfo::OnDraw() { + ImGuiWindowFlags window_flags = ImGuiChildFlags_ResizeX; + ImGui::BeginChild("ChildL", ImVec2(250, 0), true, window_flags); + { + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x/2, 20)); + + ImGui::Button("Focus Vessel"); + ImGui::SameLine(); + ImGui::Button("Camera Target"); + DrawTree(); + } + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("ChildR", ImVec2(0, 0), true); + ImVec2 button_sz(ImVec2(ImGui::GetContentRegionAvail().x, 20)); + ImGui::Text("Object: %s", m_SelectedTarget.c_str()); + + DrawInfo(); + + ImGui::EndChild(); } -// ====================================================================== - -BOOL DlgInfo::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDHELP: - DefHelpContext.topic = (char*)"/objinfo.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDC_INFO_MAP: - OpenMap(); - return TRUE; - case IDC_INFO_DDN: - ExpandAll (hDlg); - return TRUE; - case IDC_INFO_DUP: - CollapseAll (hDlg); - return TRUE; - case IDC_INFO_TYPE: - if (code == CBN_SELCHANGE) { - BuildObjectList (hDlg); - SelectionChanged (hDlg); - return TRUE; - } - break; - case IDC_INFO_NAME: - if (code == CBN_SELCHANGE) { - SelectionChanged (hDlg); - return TRUE; - } - break; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); +void DlgInfo::SetBody(Body *body) { + m_SelectedTarget = body->Name(); } diff --git a/Src/Orbiter/DlgInfo.h b/Src/Orbiter/DlgInfo.h index 1b03557d8..2a863c8df 100644 --- a/Src/Orbiter/DlgInfo.h +++ b/Src/Orbiter/DlgInfo.h @@ -7,81 +7,24 @@ #ifndef __DLGINFO_H #define __DLGINFO_H +#include "OrbiterAPI.h" -#include "DialogWin.h" -#include "DlgCtrl.h" - -class Body; class CelestialBody; +class Vessel; class Base; - -// ====================================================================== - -class DlgInfo: public DialogWin { +class Body; +class DlgInfo : public ImGuiDialog { public: - DlgInfo (HINSTANCE hInstance, HWND hParent, void *context); - void SetBody (Body *bd); - -protected: - void Init (HWND hDlg); - void BuildObjectList (HWND hDlg, Body *b = NULL); - int Size (DWORD width, DWORD height); - void ExpandAll (HWND hDlg); - void CollapseAll (HWND hDlg); - void Update (); - void SelectionChanged (HWND hDlg); - void SetBody (HWND hDlg, Body *bd); - void InitItems_vessel (HWND hDlg, const Vessel *vessel); - void InitItems_celbody (HWND hDlg, const CelestialBody *cbody); - void InitItems_base (HWND hDlg, const Base *base); - void UpdateItems_vessel (); - void UpdateItems_celbody (); - void UpdateItems_base (); - void OpenMap (); - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnSize (HWND hWnd, WPARAM wParam, int w, int h); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - -private: - Body *body; - int list_top, list_w, list_h; - int client_w, client_h; - PropertyList pl; - double upd_t; - double upd_dt; - bool showth[3]; - HICON hIcon_dd, hIcon_du; - - enum ListMode { LIST_NONE, LIST_VESSEL, LIST_CBODY, LIST_BASE } listmode; - - struct VesselList { - PropertyGroup *des; - PropertyGroup *prm; - PropertyGroup *thr; - PropertyGroup *els; - PropertyGroup *srf; - PropertyGroup *atm; - PropertyGroup *aer; - PropertyGroup *dck; - PropertyGroup *prp; - } vlist; - - struct CBodyList { - PropertyGroup *des; - PropertyGroup *prm; - PropertyGroup *atm; - PropertyGroup *els; - PropertyGroup *loc; - PropertyGroup *ecl; - PropertyGroup *prp; - } cblist; - - struct BaseList { - PropertyGroup *des; - PropertyGroup *pad; - PropertyGroup *rwy; - PropertyGroup *vor; - } blist; + DlgInfo(); + void OnDraw() override; + void AddCbodyNode(const CelestialBody *cbody); + void DrawTree(); + void DrawInfo(); + void DrawInfoVessel(Vessel *); + void DrawInfoCelestialBody(CelestialBody *); + void DrawInfoBase(Base *); + void SetBody(Body *); + std::string m_SelectedTarget; }; #endif // !__DLGINFO_H \ No newline at end of file diff --git a/Src/Orbiter/DlgMap.cpp b/Src/Orbiter/DlgMap.cpp index e8b946e3e..4e34ddde1 100644 --- a/Src/Orbiter/DlgMap.cpp +++ b/Src/Orbiter/DlgMap.cpp @@ -4,502 +4,113 @@ // ====================================================================== // "Map" dialog // ====================================================================== - -#define STRICT 1 - -#include #include "DlgMap.h" -#include "Dialogs.h" #include "Orbiter.h" #include "Psys.h" -#include "Planet.h" -#include "Resource.h" -#include "DlgMgr.h" // to be removed -#include "Pane.h" -#include "Util.h" - -using std::min; -using std::max; +#include "Celbody.h" +#include "Psys.h" +#include "imgui.h" +#include "IconsFontAwesome6.h" +#include "DlgInfo.h" -extern Orbiter *g_pOrbiter; extern TimeData td; -extern Vessel *g_focusobj; -extern PlanetarySystem *g_psys; -extern HELPCONTEXT DefHelpContext; -extern char DBG_MSG[256]; - -static int count = 0; - -inline bool GetMapPos (double lng, double lat, int &x, int &y); - -MapWin *MapWin::map_in_creation = 0; - -// ====================================================================== - -MapWin::MapWin (DlgMap *pDlg): VectorMap () -{ - dlg = pDlg; - hWnd = NULL; - bPaintPending = false; - bForceUpdate = false; - bDragActive = false; - storeflag = dispflag; - mousemode = MOUSE_DRAG; - RegisterWindow (pDlg->GetHinst()); -} - -// ====================================================================== - -MapWin::~MapWin () -{ - UnregisterWindow (dlg->GetHinst()); -} - -// ====================================================================== - -void MapWin::RegisterWindow (HINSTANCE hInst) -{ - // Register map window class - WNDCLASS wndClass; - wndClass.style = CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = Map_WndProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = hInst; - wndClass.hIcon = NULL; - wndClass.hCursor = NULL; - wndClass.hbrBackground = NULL; - wndClass.lpszMenuName = NULL; - wndClass.lpszClassName = "MapWindow"; - RegisterClass (&wndClass); -} - -// ====================================================================== - -void MapWin::UnregisterWindow (HINSTANCE hInst) -{ - UnregisterClass ("MapWindow", hInst); -} - -// ====================================================================== - -int MapWin::Create () -{ - DlgMap::MAP_PARAM *prm = &dlg->MapPrm; - font = CreateFont (-9, 0, 0, 0, 400, 0, 0, 0, 0, 3, 2, 1, 49, "Arial"); - pen[0] = CreatePen (PS_SOLID, 3, 0xffff00); - pen[1] = CreatePen (PS_SOLID, 1, 0xff0000); - - return 0; -} - -// ====================================================================== - -int MapWin::Destroy () -{ - int i; - - DeleteObject (font); - for (i = 0; i < 2; i++) DeleteObject (pen[i]); - return 0; -} - -// ====================================================================== - -void MapWin::PostCreation (HWND hw) -{ - hWnd = hw; - SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR)this); -} - -// ====================================================================== - -void MapWin::Update (bool force) -{ - const double updDT = 1.0; - static double updTmin = 0.0, updTmax = 0.0; - - if (!hWnd) return; - if (force) bForceUpdate = true; - -#ifdef ASYNC_DRAWMAP - // needs a rethink - if (!ThreadBusy()) { - bPaintPending = true; - InvalidateRect (hWnd, NULL, FALSE); - } - if (!bPaintPending && (bForceUpdate || td.SysT > updTmax || td.SysT < updTmin)) { - updTmax = td.SysT + updDT; - updTmin = td.SysT - updDT; - VectorMap::Update (); - AsyncDrawMap (); - bForceUpdate = false; - } -#else - if (bForceUpdate || td.SysT1 > updTmax || td.SysT1 < updTmin) { - updTmax = td.SysT1 + updDT; - updTmin = td.SysT1 - updDT; - VectorMap::Update (); - DrawMap(); - InvalidateRect(hWnd, NULL, FALSE); - bPaintPending = true; // ? - bForceUpdate = false; - } -#endif -} - -// ====================================================================== - -bool MapWin::SetCBody (const CelestialBody *body) -{ - bool redraw; - if (redraw = VectorMap::SetCBody (body)) { - for (int i = 0; i < mkrset.nset; i++) - mkrset.set[i].active = true; - if (hWnd) Update (true); - } - return redraw; -} - -// ====================================================================== - -int MapWin::Paint () -{ - HDC hDCtgt; - PAINTSTRUCT ps; - - hDCtgt = BeginPaint (hWnd, &ps); - if (bPaintPending) { - HDC hDCsrc = GetDeviceContext(); - HBITMAP pBmp = (HBITMAP)SelectObject (hDCsrc, GetMap()); - BitBlt (hDCtgt, 0, 0, cw, ch, hDCsrc, 0, 0, SRCCOPY); - SelectObject (hDCsrc, pBmp); - } - EndPaint (hWnd, &ps); - bPaintPending = false; - return 0; -} - -// ====================================================================== - -int MapWin::Size (DWORD w, DWORD h) -{ - HDC hDC = GetDC(NULL); - SetCanvas (hDC, w, h); - ReleaseDC (NULL, hDC); - Update (true); - return 0; -} - -// ====================================================================== - -void MapWin::Pan (int dx, int dy) -{ - double lng = lngc - dx/scalefac; - double lat = max (-Pi05, min (Pi05, latc + dy/scalefac)); - if (lng != lngc || lat != latc) { - SetCenter (lng, lat); - Update(true); - } -} - -// ====================================================================== -void MapWin::OnLButtonDown (int mx, int my) -{ - if (mousemode == MOUSE_DRAG) { - bDragActive = true; - mousex = mx; - mousey = my; - storeflag = GetDisplayFlags(); - SetDisplayFlags (storeflag & ~(DISP_NAVAID | DISP_CUSTOM1)); - Update(true); - } else { - FindTarget (mx, my); - } -} - -// ====================================================================== - -void MapWin::OnLButtonUp (int mx, int my) -{ - if (bDragActive) { - bDragActive = false; - SetDisplayFlags (storeflag); - dlg->prm_store.lngcnt = lngc; - dlg->prm_store.latcnt = latc; - Update(true); - } -} - -// ====================================================================== - -void MapWin::OnMouseMove (int mx, int my) -{ - if (GetForegroundWindow() == dlg->hWnd) SetFocus (hWnd); - SetCursor (LoadCursor (NULL, mousemode == MOUSE_DRAG ? IDC_HAND : IDC_CROSS)); - if (bDragActive) { - int dx = mx-mousex; - int dy = my-mousey; - mousex = mx; - mousey = my; - Pan (dx, dy); - } -} - -// ====================================================================== - -void MapWin::OnMouseWheel (int wdelta) -{ - if (!wdelta) return; // sanity check - double zscale; - if (wdelta > 0) - zscale = (double)wdelta/(double)WHEEL_DELTA*2.0; - else - zscale = -(double)WHEEL_DELTA/(double)wdelta*0.5; - int z = (int)(ZoomFac() * zscale + 0.5); - dlg->SetZoom (z); -} - -// ====================================================================== - -void MapWin::SetMouseMode (MouseMode mode) -{ - mousemode = mode; -} - -// ====================================================================== - -bool MapWin::FindTarget (int mx, int my) -{ - const OBJTYPE obj = FindObject (mx, my); - if (obj.type) { - SetSelection (obj); - dlg->EchoSelection (obj); - } - dlg->EnableInfo (obj.type == DISP_BASE || obj.type == DISP_VESSEL || obj.type == DISP_MOON); - - Update (true); - return (obj.type != 0); -} - -// ====================================================================== - -MapWin *MapWin::GetMapInstance (HWND hw) -{ - MapWin *map = (MapWin*)GetWindowLongPtr (hw, GWLP_USERDATA); - if (!map) map = map_in_creation; - return map; -} - -// ====================================================================== - -LRESULT FAR PASCAL MapWin::Map_WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_CREATE: - return GetMapInstance (hWnd)->Create (); - case WM_DESTROY: - return GetMapInstance (hWnd)->Destroy (); - case WM_SIZE: - return GetMapInstance (hWnd)->Size (LOWORD(lParam), HIWORD(lParam)); - case WM_PAINT: - return GetMapInstance (hWnd)->Paint (); - //case WM_LBUTTONDBLCLK: - // return GetMapInstance (hWnd)->GetInfo (LOWORD(lParam), HIWORD(lParam)); - case WM_LBUTTONDOWN: - SetCapture (hWnd); - GetMapInstance(hWnd)->OnLButtonDown (LOWORD(lParam), HIWORD(lParam)); - return 0; - case WM_LBUTTONUP: - ReleaseCapture(); - GetMapInstance(hWnd)->OnLButtonUp (LOWORD(lParam), HIWORD(lParam)); - return 0; - case WM_MOUSEMOVE: - GetMapInstance(hWnd)->OnMouseMove (LOWORD(lParam), HIWORD(lParam)); - return 0; - case WM_MOUSEWHEEL: - GetMapInstance(hWnd)->OnMouseWheel (GET_WHEEL_DELTA_WPARAM(wParam)); - return 0; - } - return DefWindowProc (hWnd, uMsg, wParam, lParam); -} - -// ====================================================================== -// ====================================================================== - -const int nbtl = 1; -const int btlid[nbtl] = {IDC_MAP_TRACK}; -const int nbtr = 4; -const int btrid[nbtr] = {IDC_MAP_INFO, IDC_MAP_OPTIONS, IDCANCEL, IDHELP}; +extern PlanetarySystem *g_psys; -DlgMap::MapPrmPersist DlgMap::prm_store = { NULL }; +DlgMap::DlgMap() : ImGuiDialog(ICON_FA_GLOBE " Orbiter: Map", {640, 400}) { + planet = "Select..."; + enableInfo = false; + searchbuf[0] = '\0'; + selectionfilter = DISP_MOON | DISP_VESSEL | DISP_BASE; -DlgMap::DlgMap (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_MAP, 0, 0, context) -{ - map = new MapWin (this); prm = &g_pOrbiter->Cfg()->CfgMapPrm; - pos = &g_pOrbiter->Cfg()->CfgWindowPos.DlgMap; - memset (&MapPrm, 0, sizeof(MAP_PARAM)); -} - -// ====================================================================== - -DlgMap::~DlgMap () -{ - prm_store.sel = map->GetSelection(); - prm_store.track = (map->GetCenterMode() != 0); - delete map; -} - -// ====================================================================== - -void DlgMap::GlobalInit () -{ - prm_store.cbody = NULL; - prm_store.sel.obj = NULL; - prm_store.sel.type = 0; - prm_store.zoom = 1; - prm_store.lngcnt = 0.0; - prm_store.latcnt = 0.0; - prm_store.track = false; -} - -#ifdef UNDEF -// ====================================================================== - -HWND DlgMap::Open () -{ - DialogManager *dlgmgr = g_pOrbiter->DlgMgr(); - if (!dlgmgr) return 0; - HINSTANCE hInst = g_pOrbiter->GetInstance(); - HWND hParent = g_pOrbiter->GetRenderWnd(); - if (dlgmgr->IsEntry (hInst, IDD_MAP)) return 0; // already open - DlgMap *dlg = new DlgMap (hInst, hParent); - return dlgmgr->AddEntry (dlg); -} -#endif - -// ====================================================================== - -HWND DlgMap::OpenWindow () -{ - MapWin::map_in_creation = map; - HWND hw = DialogWin::OpenWindow (); - HWND hMap = GetDlgItem (hw, IDC_MAP); - map->PostCreation (hMap); - MapWin::map_in_creation = 0; - return hw; -} - -// ====================================================================== - -void DlgMap::Update () -{ - map->Update(); -} - -// ====================================================================== -void DlgMap::VesselDeleting (Vessel *v) -{ - if (v == map->GetSelection().obj) { - VectorMap::OBJTYPE nullobj = {NULL,0}; - EchoSelection (nullobj); - map->UnsetSelection(); - } -} - -// ====================================================================== - -void DlgMap::SetCBody (CelestialBody *cbody) -{ - if (cbody != prm_store.cbody) { - prm_store.cbody = cbody; - map->SetCBody (cbody); - VectorMap::OBJTYPE nullobj = {NULL,0}; - EchoSelection (nullobj); - } -} - -// ====================================================================== - -void DlgMap::SetPlanet (const char *name) -{ - CelestialBody *cbody = g_psys->GetGravObj (name, true); - if (cbody != prm_store.cbody) { - prm_store.cbody = cbody; - map->SetCBody (cbody); - } -} - -// ====================================================================== - -void DlgMap::EchoSelection (const VectorMap::OBJTYPE &obj) -{ - HWND hNameBox = GetDlgItem (hWnd, IDC_MAP_FINDNAME); - switch (obj.type) { - case DISP_VESSEL: - case DISP_BASE: - case DISP_MOON: - SetWindowText (hNameBox, ((Body*)obj.obj)->Name()); - break; - case DISP_NAVAID: - SetWindowText (hNameBox, ((Nav*)obj.obj)->GetId()); - break; - default: - SetWindowText (hNameBox, ""); - break; - } -} - -// ====================================================================== - -void DlgMap::SetSelection (const Body *body) -{ - const CelestialBody *ref; - VectorMap::OBJTYPE sel; - sel.obj = body; - switch (body->Type()) { - case OBJTP_VESSEL: - sel.type = DISP_VESSEL; - ref = ((Vessel*)body)->ElRef(); - break; - case OBJTP_PLANET: - sel.type = DISP_MOON; - ref = ((CelestialBody*)body)->ElRef(); - break; - case OBJTP_STAR: - // Only way for this is to select the target as the cbody, and drop the selection - ref = (const CelestialBody*)body; - sel.obj = NULL; - sel.type = 0; - break; - case OBJTP_SURFBASE: - sel.type = DISP_BASE; - ref = ((Base*)body)->RefPlanet(); - break; - } - const CelestialBody *cbody = GetMapWin()->GetCBody(); - if (cbody != ref) { - GetMapWin()->SetCBody(ref); - CBodySelectComboBox::BuildListFromNode (GetHwnd(), IDC_MAP_REFERENCE, ref); - } - SetSelection (sel); -} - -// ====================================================================== - -void DlgMap::SetSelection (const VectorMap::OBJTYPE &obj) -{ - if (map->SetSelection (obj)) - EchoSelection (obj); -} - -// ====================================================================== - -bool DlgMap::SetSelection (const char *name, int type) + SetHelp("html/orbiter.chm", "/map.htm"); +} +void DlgMap::Display() { + ImGui::SetNextWindowSize(ImVec2(defaultSize.width, defaultSize.height), ImGuiCond_FirstUseEver); + + bool visible = ImGui::Begin(name.c_str(), &active); + HandleHelpButton(); + if(ImGui::MenuButton(ICON_FA_ARROW_ROTATE_LEFT, "Reset map", ImGui::GetFontSize()*1.5f)) { + Reset(); + } + + if(visible) { + OnDraw(); + } + ImGui::End(); + if (!active) OnClose(); +} + +void DlgMap::Reset() +{ + zoom=1.0; + vectormap->SetZoom(zoom); + vectormap->SetCenter(0.0,0.0); + vectormap->UnsetSelection(); + vectormap->Update(); + vectormap->DrawMap (); + searchbuf[0] = '\0'; + enableInfo = false; + selectionfilter = DISP_MOON | DISP_VESSEL | DISP_BASE; +} + +void DlgMap::AddCbodyNode(const CelestialBody *cbody) { + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth; + const bool is_selected = planet == cbody->Name(); + if (is_selected) + node_flags |= ImGuiTreeNodeFlags_Selected; + + if(cbody->nSecondary()) { + if(!strcmp(cbody->Name(), "Sun")) + node_flags|=ImGuiTreeNodeFlags_DefaultOpen; + + bool node_open = ImGui::TreeNodeEx(cbody->Name(), node_flags); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + planet = cbody->Name(); + SetBody(cbody->Name()); + ImGui::CloseCurrentPopup(); + } + if(node_open) { + for (int i = 0; i < cbody->nSecondary(); i++) { + AddCbodyNode (cbody->Secondary(i)); + } + ImGui::TreePop(); + } + } else { + ImGui::TreeNodeEx(cbody->Name(), node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { + planet = cbody->Name(); + SetBody(cbody->Name()); + ImGui::CloseCurrentPopup(); + } + } +} + +void DlgMap::DrawTree() { + for (int i = 0; i < g_psys->nStar(); i++) + AddCbodyNode (g_psys->GetStar(i)); +} + + +void DlgMap::SetBody(const char *body) { + planet = body; + if(vectormap) { + Planet *b = g_psys->GetPlanet (body); + if(b) { + vectormap->SetCBody(b); + vectormap->Update(); + vectormap->DrawMap (); + + for (int i = 0; i < vectormap->GetCustomMarkerSet().nset; i++) + vectormap->GetCustomMarkerSet().set[i].active = true; + + } + } +} + +bool DlgMap::SetSelection(const char *name) { if (!name[0]) return false; // sanity check @@ -510,7 +121,7 @@ bool DlgMap::SetSelection (const char *name, int type) VectorMap::OBJTYPE sel; bool found_exact = false; - if ((type == 1 || type == 0) && map->GetDisplayFlags() & DISP_VESSEL) { // search for vessel + if ((selectionfilter & DISP_VESSEL) && (vectormap->GetDisplayFlags() & DISP_VESSEL)) { // search for vessel for (i = 0; i < g_psys->nVessel(); i++) { Vessel *v = g_psys->GetVessel(i); if (!_strnicmp (v->Name(), name, len)) { @@ -524,8 +135,8 @@ bool DlgMap::SetSelection (const char *name, int type) } } } - if ((type == 2 || type == 0) && map->GetDisplayFlags() & DISP_BASE) { // search for bases - const Planet *planet = map->GetPlanet(); + if ((selectionfilter & DISP_BASE) && (vectormap->GetDisplayFlags() & DISP_BASE)) { // search for bases + const Planet *planet = vectormap->GetPlanet(); if (planet) { for (i = 0; i < planet->nBase(); i++) { const Base *base = planet->GetBase(i); @@ -541,8 +152,8 @@ bool DlgMap::SetSelection (const char *name, int type) } } } - if ((type == 3 || type == 0) && map->GetDisplayFlags() & DISP_NAVAID) { // search for VOR transmitters - const Planet *planet = map->GetPlanet(); + if ((selectionfilter & DISP_NAVAID) && vectormap->GetDisplayFlags() & DISP_NAVAID) { // search for VOR transmitters + const Planet *planet = vectormap->GetPlanet(); if (planet) { for (i = 0; i < planet->nNav(); i++) { const Nav *nav = planet->NavMgr().GetNav(i); @@ -561,9 +172,9 @@ bool DlgMap::SetSelection (const char *name, int type) } } } - if ((type == 5 || type == 0) && map->GetDisplayFlags() & DISP_MOON) { // search for moons - for (i = 0; i < map->GetCBody()->nSecondary(); i++) { - const CelestialBody *moon = map->GetCBody()->Secondary (i); + if ((selectionfilter & DISP_MOON) && vectormap->GetDisplayFlags() & DISP_MOON) { // search for moons + for (i = 0; i < vectormap->GetCBody()->nSecondary(); i++) { + const CelestialBody *moon = vectormap->GetCBody()->Secondary (i); if (!_strnicmp (moon->Name(), name, len)) { if (nhit < maxhit) hitstr[nhit] = moon->Name(); nhit++; @@ -576,401 +187,269 @@ bool DlgMap::SetSelection (const char *name, int type) } } - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_RESETCONTENT, 0, 0); - if (found_exact) { - map->SetSelection (sel); - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_ADDSTRING, 0, (LPARAM)name); - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_SHOWDROPDOWN, FALSE, 0); - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_SETCURSEL, 0, 0); - EnableInfo (sel.type == DISP_BASE || sel.type == DISP_VESSEL || sel.type == DISP_MOON); - } else { - if (nhit <= maxhit) { - for (i = 0; i < nhit; i++) - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_ADDSTRING, 0, (LPARAM)hitstr[i]); - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_SHOWDROPDOWN, nhit > 0 ? TRUE:FALSE, 0); - } - SetWindowText (GetDlgItem (hWnd, IDC_MAP_FINDNAME), name); + vectormap->SetSelection (sel); + enableInfo = (sel.type == DISP_BASE || sel.type == DISP_VESSEL || sel.type == DISP_MOON); } - SendDlgItemMessage (hWnd, IDC_MAP_FINDNAME, CB_SETEDITSEL, 0, MAKELPARAM(len,len)); - SendMessage (GetDlgItem (hWnd, IDC_MAP_FINDNAME), WM_SETCURSOR, 0, 0); return found_exact; } -// ====================================================================== - -void DlgMap::ToggleDispFlags (DWORD dflag) -{ - SetDispFlags (prm->DispFlag ^ dflag); -} - -// ====================================================================== - -void DlgMap::SetDispFlags (DWORD flag) -{ - prm->DispFlag = flag; - map->SetDisplayFlags (prm->DispFlag); - map->Update (true); -} - -// ====================================================================== - -void DlgMap::SetDragMode (bool drag) -{ - map->SetMouseMode (drag ? MapWin::MOUSE_DRAG : MapWin::MOUSE_SELECT); -} - -// ====================================================================== - -void DlgMap::EnableInfo (bool enable) -{ - EnableWindow (GetDlgItem (hWnd, IDC_MAP_INFO), enable ? 1:0); -} - -// ====================================================================== - -void DlgMap::OpenInfo () -{ - VectorMap::OBJTYPE obj = map->GetSelection (); - if (obj.type == DISP_BASE || obj.type == DISP_VESSEL || obj.type == DISP_MOON) { - DlgInfo *pInfo = g_pOrbiter->DlgMgr()->EnsureEntry (); - pInfo->SetBody ((Body*)obj.obj); - - RECT r; - GetWindowRect (hWnd, &r); - SetWindowPos (pInfo->GetHwnd(), NULL, r.right, r.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); - } -} - -// ====================================================================== - -BOOL DlgMap::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) +bool DlgMap::FindTarget(int mx, int my) { - int i; - RECT rect; - char cbuf[64]; - - SendDlgItemMessage (hDlg, IDC_MAP_SELECT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIcon (g_pOrbiter->GetInstance(), MAKEINTRESOURCE(IDI_CROSS))); - SendDlgItemMessage (hDlg, IDC_MAP_DRAG, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIcon (g_pOrbiter->GetInstance(), MAKEINTRESOURCE(IDI_FINGER))); - SendDlgItemMessage (hDlg, IDC_MAP_DRAG, BM_SETCHECK, BST_CHECKED, 0); - map->SetZoom (prm_store.zoom); - sprintf (cbuf, "Zoom %dx", prm_store.zoom); - SetWindowText (GetDlgItem (hDlg, IDC_MAP_ZOOMBOX), cbuf); - map->SetDisplayFlags (prm->DispFlag); - GetClientRect (hDlg, &rect); - MapPrm.dlgw = rect.right, MapPrm.dlgh = rect.bottom; - GetWindowRect (GetDlgItem (hDlg, IDC_MAP), &rect); - MapPrm.mapw = rect.right-rect.left, MapPrm.maph = rect.bottom-rect.top; - for (i = 0; i < nbtl; i++) { - GetWindowRect (GetDlgItem (hDlg, btlid[i]), &rect); - MapPrm.btlp[i].x = rect.left, MapPrm.btlp[i].y = rect.top; - ScreenToClient (hDlg, &MapPrm.btlp[i]); - } - for (i = 0; i < nbtr; i++) { - GetWindowRect (GetDlgItem (hDlg, btrid[i]), &rect); - MapPrm.btrp[i].x = rect.left, MapPrm.btrp[i].y = rect.top; - ScreenToClient (hDlg, &MapPrm.btrp[i]); - } + const VectorMap::OBJTYPE obj = vectormap->FindObject(mx, my); + if(obj.type) { + vectormap->SetSelection (obj); + enableInfo = (obj.type == DISP_BASE || obj.type == DISP_VESSEL || obj.type == DISP_MOON); - if (!prm_store.cbody) prm_store.cbody = g_focusobj->ProxyPlanet(); - CBodySelectComboBox::BuildListFromNode (hDlg, IDC_MAP_REFERENCE, prm_store.cbody); - map->SetCBody (prm_store.cbody); - map->SetCenter (prm_store.lngcnt, prm_store.latcnt); - if (map->SetSelection (prm_store.sel)) - EchoSelection (prm_store.sel); - if (prm_store.track) { - SendDlgItemMessage (hDlg, IDC_MAP_TRACK, BM_SETCHECK, BST_CHECKED, 0); - map->SetCenterMode (2); + switch (obj.type) { + case DISP_VESSEL: + case DISP_BASE: + case DISP_MOON: + strcpy(searchbuf, ((Body*)obj.obj)->Name()); + break; + case DISP_NAVAID: + strcpy(searchbuf, ((Nav*)obj.obj)->GetId()); + break; + default: + strcpy (searchbuf, ""); + break; + } } - SendDlgItemMessage (hDlg, IDC_MAP_FINDTYPE, CB_RESETCONTENT, 0, 0); - const char *tpstr[6] = {"Any", "Vessel", "Base", "VOR", "Marker", "Moons"}; - for (i = 0; i < 6; i++) - SendDlgItemMessage (hDlg, IDC_MAP_FINDTYPE, CB_ADDSTRING, 0, (LPARAM)tpstr[i]); - SendDlgItemMessage (hDlg, IDC_MAP_FINDTYPE, CB_SETCURSEL, 0, 0); - - return TRUE; -} - -// ====================================================================== - -BOOL DlgMap::OnSize (HWND hDlg, WPARAM wParam, int w, int h) -{ - RECT rect; - int i, dx, dy; - - GetClientRect (hWnd, &rect); - dx = rect.right - MapPrm.dlgw, dy = rect.bottom - MapPrm.dlgh; - SetWindowPos (GetDlgItem (hWnd, IDC_MAP), HWND_TOP, 0, 0, MapPrm.mapw+dx, MapPrm.maph+dy, SWP_NOMOVE); - for (i = 0; i < nbtr; i++) ShowWindow (GetDlgItem (hWnd, btrid[i]), SW_HIDE); - for (i = 0; i < nbtr; i++) - SetWindowPos (GetDlgItem (hWnd, btrid[i]), HWND_TOP, MapPrm.btrp[i].x+dx, MapPrm.btrp[i].y+dy, 0, 0, SWP_NOSIZE); - for (i = 0; i < nbtr; i++) ShowWindow (GetDlgItem (hWnd, btrid[i]), SW_SHOW); - for (i = 0; i < nbtl; i++) - SetWindowPos (GetDlgItem (hWnd, btlid[i]), HWND_TOP, MapPrm.btlp[i].x, MapPrm.btlp[i].y+dy, 0, 0, SWP_NOSIZE); - return DialogWin::OnSize (hDlg, wParam, w, h); + return (obj.type != 0); } -// ====================================================================== - -BOOL DlgMap::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - char cbuf[1024]; - int i; - - switch (id) { - case IDHELP: - DefHelpContext.topic = (char*)"/map.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDC_MAP_OPTIONS: - g_pOrbiter->DlgMgr()->EnsureEntry (0, this); - return TRUE; - case IDC_MAP_INFO: - OpenInfo(); - return TRUE; - case IDC_MAP_REFERENCE: - if (code == CBN_SELCHANGE) { - CelestialBody *cbody = CBodySelectComboBox::OnSelectionChanged (hDlg, IDC_MAP_REFERENCE); - if (cbody) SetCBody (cbody); - } - break; - case IDC_MAP_FINDTYPE: - if (code == CBN_SELCHANGE) { - SetFindMask (); - } - break; - case IDC_MAP_FINDNAME: - if (code == CBN_EDITCHANGE) { - GetWindowText (GetDlgItem (hDlg, IDC_MAP_FINDNAME), cbuf, 256); - i = SendDlgItemMessage (hDlg, IDC_MAP_FINDTYPE, CB_GETCURSEL, 0, 0); - SetSelection (cbuf, i); - } else if (code == CBN_SELCHANGE) { - i = SendDlgItemMessage (hDlg, IDC_MAP_FINDNAME, CB_GETCURSEL, 0, 0); - if (i != CB_ERR) { - SendDlgItemMessage (hDlg, IDC_MAP_FINDNAME, CB_GETLBTEXT, i, (LPARAM)cbuf); - i = SendDlgItemMessage (hDlg, IDC_MAP_FINDTYPE, CB_GETCURSEL, 0, 0); - SetSelection (cbuf, i); +void DlgMap::DrawMap() { + ImVec2 sz = ImGui::GetContentRegionAvail(); + if(sz.x>=1.0f && sz.y >= 1.0f) { + static ImVec2 oldsz = sz;//ImVec2(512,256); + + if(!vectormap) { + Planet *earth = g_psys->GetPlanet ("Earth"); + if(earth) { + planet = "Earth"; + vectormap=std::make_unique(earth); + vectormap->SetCBody(earth); + vectormap->SetCanvas(nullptr, sz.x, sz.y); + vectormap->SetDisplayFlags(prm->DispFlag); + + SetBody("Earth"); + } + } + + if(sz.x != oldsz.x || sz.y != oldsz.y) { + vectormap->SetCanvas(nullptr, sz.x, sz.y); + + vectormap->Update(); + vectormap->DrawMap (); + + oldsz = sz; + } + + const double updDT = 1.0; + if (td.SysT1 > updTmax || td.SysT1 < updTmin) { + updTmax = td.SysT1 + updDT; + updTmin = td.SysT1 - updDT; + + vectormap->Update(); + vectormap->DrawMap (); + } + + ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left + ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint + ImVec4 border_col = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); // 50% opaque white + + ImTextureID map = oapiGetImTextureID(vectormap->GetMap()); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + ImGui::ImageButton("##VectorMap", map, ImVec2(sz.x, sz.y), uv_min, uv_max, border_col, tint_col); + ImGui::PopStyleVar(); + + ImGuiIO& io = ImGui::GetIO(); + bool updateMap = false; + if (ImGui::IsItemHovered() && io.MouseWheel != 0.0) { + zoom+=io.MouseWheel; + if(zoom<1.0) zoom=1.0; + + vectormap->SetZoom(zoom); + updateMap = true; + } + if (ImGui::IsItemActive()) { + if(ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + ImVec2 mousepos = ImGui::GetMousePos(); + ImVec2 imagepos = ImGui::GetItemRectMin(); + if(FindTarget(mousepos.x-imagepos.x, mousepos.y-imagepos.y)) + updateMap = true; + } else { + double lngc = vectormap->CntLng(); + double latc = vectormap->CntLat(); + + double scale = std::min (sz.x, 2.0f*sz.y); + double scalefac = vectormap->ZoomFac()*scale/Pi2; + + double lng = lngc - io.MouseDelta.x/scalefac; + double lat = std::max (-Pi05, std::min (Pi05, latc + io.MouseDelta.y/scalefac)); + if (lng != lngc || lat != latc) { + vectormap->SetCenter (lng, lat); + updateMap = true; + } } - } else if (code == CBN_SELENDCANCEL) { - i = SendDlgItemMessage (hDlg, IDC_MAP_FINDNAME, CB_GETCURSEL, 0, 0); - if (i == CB_ERR) return 0; + } + + if(updateMap) { + vectormap->Update(); + vectormap->DrawMap (); + } + } +} + +void DlgMap::DrawMenu() { + ImGui::SetNextItemWidth(160.0f); + if(ImGui::BeginCombo("##dlgmap_planet", planet.c_str(), ImGuiComboFlags_HeightLargest)) { + DrawTree(); + ImGui::EndCombo(); + } + + ImGui::SameLine(); + + ImGui::SetNextItemWidth(160.0f); + if(ImGui::BeginCombo("##dlgmap_options", "Options", ImGuiComboFlags_HeightLargest)) { + int df = vectormap->GetDisplayFlags(); + int olddf = df; + + ImGui::Text("Vessels"); + int vesselmode = (df & DISP_VESSEL ? df & DISP_FOCUSONLY ? 1:0:2); + ImGui::RadioButton("All", &vesselmode, 0); ImGui::SameLine(); + ImGui::RadioButton("Focus Only", &vesselmode, 1); ImGui::SameLine(); + ImGui::RadioButton("None", &vesselmode, 2); + ImGui::Separator(); + int vmode = (vesselmode==1 ? DISP_VESSEL | DISP_FOCUSONLY : vesselmode==2 ? 0 : DISP_VESSEL); + df &= ~(DISP_VESSEL | DISP_FOCUSONLY); + df |= vmode; + + ImGui::Text("Orbit Display"); + if (ImGui::BeginTable("table orbit display", 2, ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::CheckboxFlags("Focus Vessel", &df, DISP_ORBITFOCUS); + ImGui::TableSetColumnIndex(1); + ImGui::CheckboxFlags("Target", &df, DISP_ORBITSEL); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + int orbitmode=(df & DISP_ORBITPLANE) ? 0 : 1; + ImGui::RadioButton("Orbit Plane", &orbitmode, 0); + ImGui::TableSetColumnIndex(1); + ImGui::RadioButton("Ground Track", &orbitmode, 1); + df &= ~(DISP_GROUNDTRACK | DISP_ORBITPLANE); + df |= (orbitmode == 0)? DISP_ORBITPLANE:DISP_GROUNDTRACK; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::CheckboxFlags("Horizon Line", &df, DISP_HORIZONLINE); + ImGui::EndTable(); + } + ImGui::Separator(); + + ImGui::Text("Terminator"); + if (ImGui::BeginTable("table1", 2, ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::CheckboxFlags("Line", &df, DISP_TERMINATOR_LINE); + ImGui::TableSetColumnIndex(1); + ImGui::CheckboxFlags("Shaded", &df, DISP_TERMINATOR_SHADE); + ImGui::EndTable(); + } + ImGui::Separator(); + + ImGui::Text("Surface Markers"); + if (ImGui::BeginTable("table2", 2, ImGuiTableFlags_SizingStretchSame)) { + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::CheckboxFlags("Grid Line", &df, DISP_GRIDLINE); + ImGui::TableSetColumnIndex(1); + ImGui::CheckboxFlags("Surface Bases", &df, DISP_BASE); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::CheckboxFlags("Coastlines", &df, DISP_COASTLINE); + ImGui::TableSetColumnIndex(1); + ImGui::CheckboxFlags("VOR Transmitters", &df, DISP_NAVAID); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::CheckboxFlags("Contour Lines", &df,DISP_CONTOURS); + ImGui::TableSetColumnIndex(1); + ImGui::CheckboxFlags("Landmarks", &df, DISP_CUSTOMMARKER); + + ImGui::EndTable(); + } + ImGui::Separator(); + + ImGui::Text("Other"); + ImGui::CheckboxFlags("Natural Satellites", &df, DISP_MOON); + + ImGui::EndCombo(); + + vectormap->SetDisplayFlags(df); + + if(df!=olddf) { + vectormap->Update(); + vectormap->DrawMap (); + prm->DispFlag = df; } - break; - case IDC_MAP_TRACK: - if (code == BN_CLICKED) { - bool track = (SendDlgItemMessage (hDlg, IDC_MAP_TRACK, BM_GETCHECK, 0, 0) == BST_CHECKED); - map->SetCenterMode (track ? 2:0); - } - return TRUE; - case IDC_MAP_DRAG: - case IDC_MAP_SELECT: - if (code == BN_CLICKED) - SetDragMode (id == IDC_MAP_DRAG); - return TRUE; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); -} - -// ====================================================================== - -BOOL DlgMap::OnNotify (HWND hWnd, int idCtrl, LPNMHDR pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - NMUPDOWN *nmud = (NMUPDOWN*)pnmh; - switch (pnmh->idFrom) { - case IDC_MAP_ZOOM: - if (nmud->iDelta < 0) ZoomIn(); - else ZoomOut(); - break; + } + + ImGui::SameLine(); + + if(ImGui::Button(ICON_FA_FILTER)) { + ImGui::OpenPopup("dlgmap_search_filter"); + } + ImVec2 btn_pos = ImGui::GetItemRectMin(); + btn_pos.x-=4; + btn_pos.y-=5; + ImGui::SetNextWindowPos(btn_pos); + + if (ImGui::BeginPopup("dlgmap_search_filter")) + { + ImGui::Text(ICON_FA_FILTER " Search filter"); + ImGui::Separator(); + + ImGui::CheckboxFlags("Moons", &selectionfilter, DISP_MOON); + ImGui::CheckboxFlags("Vessels", &selectionfilter, DISP_VESSEL); + ImGui::CheckboxFlags("VOR", &selectionfilter, DISP_NAVAID); + ImGui::CheckboxFlags("Landmarks", &selectionfilter, DISP_CUSTOMMARKER); + ImGui::CheckboxFlags("Bases", &selectionfilter, DISP_BASE); + + ImGui::EndPopup(); + } + + ImGui::SameLine(); + ImGui::SetNextItemWidth(160); + if(ImGui::InputText("##search", searchbuf, sizeof(searchbuf), ImGuiInputTextFlags_EnterReturnsTrue)) { + SetSelection(searchbuf); + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_MAGNIFYING_GLASS)) { + SetSelection(searchbuf); + } + ImGui::SetItemTooltip("Search object"); + + ImGui::BeginDisabled(!enableInfo); + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_CIRCLE_INFO)) { + VectorMap::OBJTYPE obj = vectormap->GetSelection (); + if (obj.type == DISP_BASE || obj.type == DISP_VESSEL || obj.type == DISP_MOON) { + DlgInfo *pInfo = g_pOrbiter->DlgMgr()->EnsureEntry (); + pInfo->SetBody ((Body*)obj.obj); } } - return DialogWin::OnNotify (hWnd, idCtrl, pnmh); -} - -// ====================================================================== - -BOOL DlgMap::OnUserMessage (HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - switch (wParam) { - case MSG_KILLVESSEL: - VesselDeleting ((Vessel*)lParam); - break; - } - return DialogWin::OnUserMessage (hWnd, wParam, lParam); -} - -// ====================================================================== - -BOOL DlgMap::OnMouseWheel (HWND hDlg, int vk, int dist, int x, int y) -{ - PostMessage (GetDlgItem (hDlg, IDC_MAP), WM_MOUSEWHEEL, MAKEWPARAM(vk,dist), MAKELPARAM(x,y)); - return 1; -} - -// ====================================================================== - -void DlgMap::SetZoom (int zoom) -{ - char cbuf[32]; - zoom = max(1, min (128, zoom)); - if (zoom != prm_store.zoom) { - map->SetZoom (zoom); - prm_store.zoom = zoom; - map->Update (true); - sprintf (cbuf, "Zoom %dx", zoom); - SetWindowText (GetDlgItem (hWnd, IDC_MAP_ZOOMBOX), cbuf); - InvalidateRect (GetDlgItem (hWnd, IDC_MAP), NULL, FALSE); - } -} - -// ====================================================================== - -void DlgMap::ZoomIn () -{ - SetZoom (prm_store.zoom*2); + if(enableInfo) + ImGui::SetItemTooltip("Open info dialog for object"); + ImGui::EndDisabled(); } -// ====================================================================== - -void DlgMap::ZoomOut () -{ - SetZoom (prm_store.zoom/2); -} - -// ====================================================================== - -void DlgMap::SetFindMask () -{ - DWORD mask; - int idx = SendDlgItemMessage (hWnd, IDC_MAP_FINDTYPE, CB_GETCURSEL, 0, 0); - switch (idx) { - case 0: mask = DISP_NAVAID | DISP_BASE | DISP_CUSTOM1 | DISP_VESSEL | DISP_MOON; break; - case 1: mask = DISP_VESSEL; break; - case 2: mask = DISP_BASE; break; - case 3: mask = DISP_NAVAID; break; - case 4: mask = DISP_CUSTOM1; break; - case 5: mask = DISP_MOON; break; - } - map->SetFindFlags (mask); -} - -// ====================================================================== -// ====================================================================== - -DlgMapOpt::DlgMapOpt (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_MAP_CONFIG, 0, 0, context) -{ - dlgmap = (DlgMap*)context; -} - -// ====================================================================== - -BOOL DlgMapOpt::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - DWORD dispflag = GetMapDlg()->GetMapWin()->GetDisplayFlags(); - SendDlgItemMessage (hDlg, IDC_MAPOPT_ORBITFOCUS, BM_SETCHECK, dispflag & DISP_ORBITFOCUS ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_ORBITSEL, BM_SETCHECK, dispflag & DISP_ORBITSEL ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_ORBITPLANE, BM_SETCHECK, dispflag & DISP_ORBITPLANE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_ORBITGTRACK, BM_SETCHECK, dispflag & DISP_GROUNDTRACK ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_HORIZON, BM_SETCHECK, dispflag & DISP_HORIZONLINE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_TLINE, BM_SETCHECK, dispflag & DISP_TERMINATOR_LINE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_TSHADE, BM_SETCHECK, dispflag & DISP_TERMINATOR_SHADE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_GRIDLINES, BM_SETCHECK, dispflag & DISP_GRIDLINE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_COASTLINES, BM_SETCHECK, dispflag & DISP_COASTLINE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_CONTOURS, BM_SETCHECK, dispflag & DISP_CONTOURS ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_BASES, BM_SETCHECK, dispflag & DISP_BASE ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_VORS, BM_SETCHECK, dispflag & DISP_NAVAID ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_LANDMARKS, BM_SETCHECK, dispflag & DISP_CUSTOM1 ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MAPOPT_SATELLITES, BM_SETCHECK, dispflag & DISP_MOON ? BST_CHECKED:BST_UNCHECKED, 0); - int i, vesselmode = (dispflag & DISP_VESSEL ? dispflag & DISP_FOCUSONLY ? 1:0:2); - const int modeid[3] = {IDC_MAPOPT_VESSELALL, IDC_MAPOPT_VESSELFOCUS, IDC_MAPOPT_VESSELNONE}; - for (i = 0; i < 3; i++) - SendDlgItemMessage (hDlg, modeid[i], BM_SETCHECK, i==vesselmode ? BST_CHECKED:BST_UNCHECKED, 0); - return 0; -} - -// ====================================================================== - -BOOL DlgMapOpt::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDC_MAPOPT_VESSELALL: - case IDC_MAPOPT_VESSELFOCUS: - case IDC_MAPOPT_VESSELNONE: { - int i, vmode; - const int modeid[3] = {IDC_MAPOPT_VESSELALL, IDC_MAPOPT_VESSELFOCUS, IDC_MAPOPT_VESSELNONE}; - for (i = 0; i < 3; i++) - if (SendDlgItemMessage (hDlg, modeid[i], BM_GETCHECK, 0, 0) == BST_CHECKED) - break; - vmode = (i==1 ? DISP_VESSEL | DISP_FOCUSONLY : i==2 ? 0 : DISP_VESSEL); - SetVesselMode (vmode); - } return TRUE; - case IDC_MAPOPT_ORBITFOCUS: - ToggleDispFlag (DISP_ORBITFOCUS); - return TRUE; - case IDC_MAPOPT_ORBITSEL: - ToggleDispFlag (DISP_ORBITSEL); - return TRUE; - case IDC_MAPOPT_ORBITPLANE: - case IDC_MAPOPT_ORBITGTRACK: - SetOrbitMode (SendDlgItemMessage (hDlg, IDC_MAPOPT_ORBITPLANE, BM_GETCHECK, 0, 0) == BST_CHECKED ? DISP_ORBITPLANE : DISP_GROUNDTRACK); - return TRUE; - case IDC_MAPOPT_HORIZON: - ToggleDispFlag (DISP_HORIZONLINE); - return TRUE; - case IDC_MAPOPT_TLINE: - ToggleDispFlag (DISP_TERMINATOR_LINE); - return TRUE; - case IDC_MAPOPT_TSHADE: - ToggleDispFlag (DISP_TERMINATOR_SHADE); - return TRUE; - case IDC_MAPOPT_GRIDLINES: - ToggleDispFlag (DISP_GRIDLINE); - return TRUE; - case IDC_MAPOPT_COASTLINES: - ToggleDispFlag (DISP_COASTLINE); - return TRUE; - case IDC_MAPOPT_CONTOURS: - ToggleDispFlag (DISP_CONTOURS); - return TRUE; - case IDC_MAPOPT_BASES: - ToggleDispFlag (DISP_BASE); - return TRUE; - case IDC_MAPOPT_VORS: - ToggleDispFlag (DISP_NAVAID); - return TRUE; - case IDC_MAPOPT_LANDMARKS: - ToggleDispFlag (DISP_CUSTOM1); - return TRUE; - case IDC_MAPOPT_SATELLITES: - ToggleDispFlag (DISP_MOON); - return TRUE; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); -} - -// ====================================================================== - -void DlgMapOpt::ToggleDispFlag (DWORD flag) -{ - dlgmap->ToggleDispFlags (flag); -} - -// ====================================================================== - -void DlgMapOpt::SetOrbitMode (DWORD flag) -{ - DWORD dflag = dlgmap->GetMapWin()->GetDisplayFlags(); - dflag &= ~(DISP_ORBITPLANE|DISP_GROUNDTRACK); - dlgmap->SetDispFlags (dflag | flag); -} - -// ====================================================================== - -void DlgMapOpt::SetVesselMode (DWORD flag) -{ - DWORD dflag = dlgmap->GetMapWin()->GetDisplayFlags(); - dflag &= ~(DISP_VESSEL | DISP_FOCUSONLY); - dlgmap->SetDispFlags (dflag | flag); +void DlgMap::OnDraw() { + DrawMenu(); + DrawMap(); } diff --git a/Src/Orbiter/DlgMap.h b/Src/Orbiter/DlgMap.h index e0b058cdf..08651a10a 100644 --- a/Src/Orbiter/DlgMap.h +++ b/Src/Orbiter/DlgMap.h @@ -8,135 +8,32 @@ #ifndef __DLGMAP_H #define __DLGMAP_H -#include "DialogWin.h" +#include "DlgMgr.h" #include "VectorMap.h" -// ====================================================================== -// Class for map window inside the dialog box - -class MapWin: public VectorMap { - friend class DlgMap; - -public: - MapWin (DlgMap *pDlg); - ~MapWin (); - - int Create (); - int Destroy (); - void PostCreation (HWND hw); - void Update (bool force = false); - bool SetCBody (const CelestialBody *body); - int Paint (); - int Size (DWORD w, DWORD h); - void Pan (int dx, int dy); - -protected: - enum MouseMode { - MOUSE_DRAG, MOUSE_SELECT - }; - - void RegisterWindow (HINSTANCE hInst); - void UnregisterWindow (HINSTANCE hInst); - - void OnLButtonDown (int mx, int my); - void OnLButtonUp (int mx, int my); - void OnMouseMove (int mx, int my); - void OnMouseWheel (int wdelta); - void SetMouseMode (MouseMode mode); - bool FindTarget (int mx, int my); - static MapWin *GetMapInstance (HWND hw); - static MapWin *map_in_creation; - static LRESULT FAR PASCAL Map_WndProc (HWND, UINT, WPARAM, LPARAM); - -private: - DlgMap *dlg; - HWND hWnd; - HFONT font; - HPEN pen[3]; - bool bPaintPending; - bool bForceUpdate; - bool bDragActive; - int mousex, mousey; - MouseMode mousemode; - DWORD storeflag; -}; - -// ====================================================================== -// Map dialog - -class DlgMap: public DialogWin { - friend class MapWin; - friend class DlgMapOpt; - +class CelestialBody; +class DlgMap : public ImGuiDialog { + void Reset(); public: - DlgMap (HINSTANCE hInstance, HWND hParent, void *context); - ~DlgMap (); - static void GlobalInit (); - void SetSelection (const Body *body); - HWND OpenWindow (); - void Update (); - void VesselDeleting (Vessel *v); - void SetCBody (CelestialBody *cbody); - void SetPlanet (const char *name); - void EchoSelection (const VectorMap::OBJTYPE &obj); - void SetSelection (const VectorMap::OBJTYPE &obj); - bool SetSelection (const char *name, int type = 0); - void ToggleDispFlags (DWORD dflag); - void SetDispFlags (DWORD flag); - void SetDragMode (bool drag); - void EnableInfo (bool enable); - void OpenInfo (); - MapWin *GetMapWin() { return map; } - - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - BOOL OnNotify (HWND hWnd, int idCtrl, LPNMHDR pnmh); - BOOL OnUserMessage (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnMouseWheel (HWND hWnd, int vk, int dist, int x, int y); - - struct MapPrmPersist { - const CelestialBody *cbody; - VectorMap::OBJTYPE sel; - int zoom; - double lngcnt, latcnt; - bool track; - }; - - struct MAP_PARAM { - int dlgw, dlgh, mapw, maph; - POINT btlp[1], btrp[4]; - }; - -protected: - BOOL OnSize (HWND hDlg, WPARAM wParam, int w, int h); - void SetZoom (int zoom); - void ZoomIn (); - void ZoomOut (); - void SetFindMask (); - -private: - MapWin *map; - MAP_PARAM MapPrm; + DlgMap(); + void Display() override; + void OnDraw() override; + void DrawMap(); + void DrawMenu(); + void SetBody(const char *body); + void AddCbodyNode(const CelestialBody *cbody); + void DrawTree(); + bool FindTarget(int mx, int my); + bool SetSelection(const char *name); + + std::unique_ptr vectormap; + double updTmin = 0.0, updTmax = 0.0; + std::string planet; + double zoom = 1.0; + bool enableInfo; + int selectionfilter; + char searchbuf[128]; CFG_MAPPRM *prm; - static MapPrmPersist prm_store; -}; - -// ====================================================================== -// Map options dialog - -class DlgMapOpt: public DialogWin { -public: - DlgMapOpt (HINSTANCE hInstance, HWND hParent, void *context); - DlgMap *GetMapDlg() { return dlgmap; } - void ToggleDispFlag (DWORD flag); - void SetOrbitMode (DWORD flag); - void SetVesselMode (DWORD flag); - - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - -private: - DlgMap *dlgmap; }; -#endif // !__DLGMAP_H \ No newline at end of file +#endif // !__DLGMAP_H diff --git a/Src/Orbiter/DlgMenuCfg.cpp b/Src/Orbiter/DlgMenuCfg.cpp index 765c65f48..edc8ff876 100644 --- a/Src/Orbiter/DlgMenuCfg.cpp +++ b/Src/Orbiter/DlgMenuCfg.cpp @@ -5,194 +5,82 @@ // Menu bar configuration dialog // ====================================================================== -#define STRICT 1 - -#include "Orbiter.h" -#include "Pane.h" -#include "MenuInfoBar.h" #include "DlgMenuCfg.h" -#include "Resource.h" -#include "Resource2.h" -#include "DlgCtrl.h" +#include "MenuInfoBar.h" +#include "Pane.h" +#include "imgui.h" +#include "imgui_extras.h" +#include "Orbiter.h" +#include "IconsFontAwesome6.h" extern Orbiter *g_pOrbiter; extern Pane *g_pane; -extern HELPCONTEXT DefHelpContext; - -// ====================================================================== - -DlgMenuCfg::DlgMenuCfg (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_MENU_CONFIG, 0, 0, context) -{ -} - -// ====================================================================== -DlgMenuCfg::~DlgMenuCfg () +DlgMenuCfg::DlgMenuCfg(): ImGuiDialog(ICON_FA_SLIDERS " Orbiter: Configure menu bars") { + SetHelp("html/orbiter.chm", "/menucfg.htm"); } -// ====================================================================== - -BOOL DlgMenuCfg::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - SendDlgItemMessage (hDlg, IDC_MNUCFG_SHOWMENU + g_pOrbiter->Cfg()->CfgUIPrm.MenuMode, - BM_SETCHECK, BST_CHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MNUCFG_SHOWINFO + g_pOrbiter->Cfg()->CfgUIPrm.InfoMode, - BM_SETCHECK, BST_CHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MNUCFG_LABELONLY, BM_SETCHECK, - g_pOrbiter->Cfg()->CfgUIPrm.bMenuLabelOnly ? BST_CHECKED : BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MNUCFG_SHOWWARP, BM_SETCHECK, - g_pOrbiter->Cfg()->CfgUIPrm.bWarpAlways ? BST_CHECKED : BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_MNUCFG_SCIWARP, BM_SETCHECK, - g_pOrbiter->Cfg()->CfgUIPrm.bWarpScientific ? BST_CHECKED : BST_UNCHECKED, 0); - for (int i = 0; i < 2; i++) { - int id = IDC_MNUCFG_LINFO+i; - SendDlgItemMessage (hDlg, id, CB_RESETCONTENT, 0, 0); - SendDlgItemMessage (hDlg, id, CB_ADDSTRING, 0, (LPARAM)"None"); - SendDlgItemMessage (hDlg, id, CB_ADDSTRING, 0, (LPARAM)"Frame rate"); - SendDlgItemMessage (hDlg, id, CB_ADDSTRING, 0, (LPARAM)"Render statistics"); - SendDlgItemMessage (hDlg, id, CB_ADDSTRING, 0, (LPARAM)"Viewport info"); - SendDlgItemMessage (hDlg, id, CB_SETCURSEL, g_pOrbiter->Cfg()->CfgUIPrm.InfoAuxIdx[i], 0); - } - SendDlgItemMessage (hDlg, IDC_PAUSEINDICATOR, CB_RESETCONTENT, 0, 0); - SendDlgItemMessage (hDlg, IDC_PAUSEINDICATOR, CB_ADDSTRING, 0, (LPARAM)"Flash on action"); - SendDlgItemMessage (hDlg, IDC_PAUSEINDICATOR, CB_ADDSTRING, 0, (LPARAM)"Show on pause/record/playback"); - SendDlgItemMessage (hDlg, IDC_PAUSEINDICATOR, CB_ADDSTRING, 0, (LPARAM)"Don't show"); - SendDlgItemMessage (hDlg, IDC_PAUSEINDICATOR, CB_SETCURSEL, g_pOrbiter->Cfg()->CfgUIPrm.PauseIndMode, 0); - - GAUGEPARAM gp1 = { 0, 10, GAUGEPARAM::LEFT, GAUGEPARAM::BLACK }; - oapiSetGaugeParams (GetDlgItem (hDlg, IDC_MNUCFG_MENUOPACITY), &gp1); - int scl = g_pOrbiter->Cfg()->CfgUIPrm.MenuOpacity; - oapiSetGaugePos (GetDlgItem (hDlg, IDC_MNUCFG_MENUOPACITY), scl); - - GAUGEPARAM gp2 = { 0, 10, GAUGEPARAM::LEFT, GAUGEPARAM::BLACK }; - oapiSetGaugeParams (GetDlgItem (hDlg, IDC_MNUCFG_INFOOPACITY), &gp2); - scl = g_pOrbiter->Cfg()->CfgUIPrm.InfoOpacity; - oapiSetGaugePos (GetDlgItem (hDlg, IDC_MNUCFG_INFOOPACITY), scl); - - GAUGEPARAM gp3 = { 1, 20, GAUGEPARAM::LEFT, GAUGEPARAM::BLACK }; - oapiSetGaugeParams (GetDlgItem (hDlg, IDC_MNUCFG_SCROLLSPEED), &gp3); - scl = g_pOrbiter->Cfg()->CfgUIPrm.MenuScrollspeed; - oapiSetGaugePos (GetDlgItem (hDlg, IDC_MNUCFG_SCROLLSPEED), scl); - - return TRUE; -} - -// ====================================================================== - -BOOL DlgMenuCfg::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDHELP: - DefHelpContext.topic = (char*)"/menucfg.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDC_MNUCFG_SHOWMENU: - case IDC_MNUCFG_SHOWMENU+1: - case IDC_MNUCFG_SHOWMENU+2: - if (code == BN_CLICKED) { - int mode = id-IDC_MNUCFG_SHOWMENU; - g_pOrbiter->Cfg()->CfgUIPrm.MenuMode = mode; - g_pane->MIBar()->SetMenuMode (mode); - } - break; - case IDC_MNUCFG_SHOWINFO: - case IDC_MNUCFG_SHOWINFO+1: - case IDC_MNUCFG_SHOWINFO+2: - if (code == BN_CLICKED) { - int mode = id-IDC_MNUCFG_SHOWINFO; - g_pOrbiter->Cfg()->CfgUIPrm.InfoMode = mode; - g_pane->MIBar()->SetInfoMode (mode); - return TRUE; - } - break; - case IDC_MNUCFG_LABELONLY: - if (code == BN_CLICKED) { - bool check = (SendDlgItemMessage (hDlg, id, BM_GETCHECK, 0, 0) == TRUE); - g_pOrbiter->Cfg()->CfgUIPrm.bMenuLabelOnly = check; - g_pane->MIBar()->SetLabelOnly (check); - return TRUE; - } - break; - case IDC_MNUCFG_SHOWWARP: - if (code == BN_CLICKED) { - bool check = (SendDlgItemMessage (hDlg, id, BM_GETCHECK, 0, 0) == TRUE); - g_pOrbiter->Cfg()->CfgUIPrm.bWarpAlways = check; - g_pane->MIBar()->SetWarpAlways (check); - return TRUE; - } - break; - case IDC_MNUCFG_SCIWARP: - if (code == BN_CLICKED) { - bool check = (SendDlgItemMessage (hDlg, id, BM_GETCHECK, 0, 0) == TRUE); - g_pOrbiter->Cfg()->CfgUIPrm.bWarpScientific = check; - g_pane->MIBar()->SetWarpScientific (check); - return TRUE; - } - break; - case IDC_MNUCFG_LINFO: - case IDC_MNUCFG_RINFO: - if (code == CBN_SELCHANGE) { - int side = id-IDC_MNUCFG_LINFO; - int idx = SendDlgItemMessage (hDlg, IDC_MNUCFG_LINFO+side, CB_GETCURSEL, 0, 0); - if (idx != CB_ERR) g_pOrbiter->Cfg()->CfgUIPrm.InfoAuxIdx[side] = idx; - if (idx && g_pOrbiter->Cfg()->CfgUIPrm.InfoAuxIdx[1-side] == idx) { - g_pOrbiter->Cfg()->CfgUIPrm.InfoAuxIdx[1-side] = 0; - g_pane->MIBar()->SetAuxInfobar (1-side,0); - SendDlgItemMessage (hDlg, IDC_MNUCFG_LINFO+1-side, CB_SETCURSEL, 0, 0); - } - g_pane->MIBar()->SetAuxInfobar (side,idx); - return TRUE; - } - break; - case IDC_PAUSEINDICATOR: - if (code == CBN_SELCHANGE) { - int idx = SendDlgItemMessage (hDlg, IDC_PAUSEINDICATOR, CB_GETCURSEL, 0, 0); - if (idx != CB_ERR && idx != g_pOrbiter->Cfg()->CfgUIPrm.PauseIndMode) { - g_pOrbiter->Cfg()->CfgUIPrm.PauseIndMode = idx; - g_pane->MIBar()->SetPauseIndicatorMode (idx); - } - } - } - return DialogWin::OnCommand (hDlg, id, code, hControl); -} - -// ====================================================================== - -BOOL DlgMenuCfg::OnHScroll (HWND hDlg, WORD request, WORD curpos, HWND hControl) +void DlgMenuCfg::OnDraw() { - switch (GetDlgCtrlID (hControl)) { - case IDC_MNUCFG_MENUOPACITY: - switch (request) { - case SB_THUMBTRACK: - case SB_LINELEFT: - case SB_LINERIGHT: - g_pOrbiter->Cfg()->CfgUIPrm.MenuOpacity = curpos; - g_pane->MIBar()->SetOpacity (curpos); - return 0; - } - break; - case IDC_MNUCFG_INFOOPACITY: - switch (request) { - case SB_THUMBTRACK: - case SB_LINELEFT: - case SB_LINERIGHT: - g_pOrbiter->Cfg()->CfgUIPrm.InfoOpacity = curpos; - g_pane->MIBar()->SetOpacityInfo (curpos); - return 0; - } - break; - case IDC_MNUCFG_SCROLLSPEED: - switch (request) { - case SB_THUMBTRACK: - case SB_LINELEFT: - case SB_LINERIGHT: - g_pOrbiter->Cfg()->CfgUIPrm.MenuScrollspeed = curpos; - g_pane->MIBar()->SetScrollspeed (curpos); - return 0; - } - break; - } - return DialogWin::OnHScroll (hDlg, request, curpos, hControl); + const char *modes[] = {"Show", "Hide", "Auto-hide"}; + + CFG_UIPRM &prm = g_pOrbiter->Cfg()->CfgUIPrm; + MenuInfoBar *mib = g_pane->MIBar(); + + ImGui::BeginGroupPanel("Menu bar"); + ImGui::PushID(1); + ImGui::RadioButton("Show", &prm.MenuMode, 0); + ImGui::SameLine(); + ImGui::RadioButton("Hide", &prm.MenuMode, 1); + ImGui::SameLine(); + ImGui::RadioButton("Auto-hide", &prm.MenuMode, 2); + mib->SetMenuMode (prm.MenuMode); + + if(ImGui::Checkbox("Labels only", &prm.bMenuLabelOnly)) + mib->SetLabelOnly (prm.bMenuLabelOnly); + + if(ImGui::SliderInt("Opacity", &prm.MenuOpacity, 0, 10)) + mib->SetOpacity (prm.MenuOpacity); + + if(ImGui::SliderInt("Scroll speed", &prm.MenuScrollspeed, 1, 20)) + mib->SetScrollspeed (prm.MenuScrollspeed); + ImGui::PopID(); + ImGui::EndGroupPanel(); + + + ImGui::BeginGroupPanel("Info bars"); + ImGui::PushID(2); + ImGui::RadioButton("Show", &prm.InfoMode, 0); + ImGui::SameLine(); + ImGui::RadioButton("Hide", &prm.InfoMode, 1); + ImGui::SameLine(); + ImGui::RadioButton("Auto-hide", &prm.InfoMode, 2); + mib->SetInfoMode (prm.InfoMode); + + if(ImGui::Checkbox("Always show warp factor", &prm.bWarpAlways)) + mib->SetWarpAlways (prm.bWarpAlways); + + if(ImGui::Checkbox("Use scientific notation for warp", &prm.bWarpScientific)) + mib->SetWarpScientific (prm.bWarpScientific); + + if(ImGui::SliderInt("Opacity", &prm.InfoOpacity, 0, 10)) + mib->SetOpacityInfo (prm.InfoOpacity); + ImGui::PopID(); + ImGui::EndGroupPanel(); + + ImGui::BeginGroupPanel("Auxiliary info bars"); + const char *auxmode[]={"None", "Frame rate", "Render statistics", "Viewport info"}; + ImGui::Combo("Left", &prm.InfoAuxIdx[0], auxmode, IM_ARRAYSIZE(auxmode)); + mib->SetAuxInfobar (0, prm.InfoAuxIdx[0]); + ImGui::Combo("Right", &prm.InfoAuxIdx[1], auxmode, IM_ARRAYSIZE(auxmode)); + mib->SetAuxInfobar (1, prm.InfoAuxIdx[1]); + + ImGui::EndGroupPanel(); + + ImGui::BeginGroupPanel("Action indicator"); + const char *actionmode[]={"Flash on action", "Show on pause/record/playback", "Don't show"}; + ImGui::Combo("Mode", &prm.PauseIndMode, actionmode, IM_ARRAYSIZE(actionmode)); + mib->SetPauseIndicatorMode (prm.PauseIndMode); + ImGui::EndGroupPanel(); } diff --git a/Src/Orbiter/DlgMenuCfg.h b/Src/Orbiter/DlgMenuCfg.h index 90212d7aa..e8954dc50 100644 --- a/Src/Orbiter/DlgMenuCfg.h +++ b/Src/Orbiter/DlgMenuCfg.h @@ -8,15 +8,12 @@ #ifndef __DLGMENUCFG_H #define __DLGMENUCFG_H -#include "DialogWin.h" +#include "OrbiterAPI.h" -class DlgMenuCfg: public DialogWin { +class DlgMenuCfg: public ImGuiDialog { public: - DlgMenuCfg (HINSTANCE hInstance, HWND hParent, void *context); - ~DlgMenuCfg (); - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - BOOL OnHScroll (HWND hWnd, WORD request, WORD curpos, HWND hControl); + DlgMenuCfg (); + void OnDraw(); }; #endif // !__DLGMENUCFG_H \ No newline at end of file diff --git a/Src/Orbiter/DlgMgr.cpp b/Src/Orbiter/DlgMgr.cpp index c4ae7e0bb..f893ffd55 100644 --- a/Src/Orbiter/DlgMgr.cpp +++ b/Src/Orbiter/DlgMgr.cpp @@ -1,12 +1,24 @@ -// Copyright (c) Martin Schweiger +// Copyright (c) Martin Schweiger // Licensed under the MIT License +#define EXPORT_IMGUI_CONTEXT +#define OAPI_IMPLEMENTATION +#define IMGUI_DEFINE_MATH_OPERATORS #include -#include "GraphicsAPI.h" +#include "OrbiterAPI.h" #include "DlgMgr.h" #include "Resource.h" #include "Orbiter.h" #include "Log.h" +#include "imgui.h" +#include "imgui_extras.h" +#include "imgui_impl_win32.h" +#include "imgui_impl_sdl3.h" +#include "IconsFontAwesome6.h" +#include +#include +#include +#include using namespace oapi; @@ -19,6 +31,7 @@ static int x_fixedframe = GetSystemMetrics (SM_CXFIXEDFRAME); static int y_fixedframe = GetSystemMetrics (SM_CYFIXEDFRAME); static bool doflip = true; +static void RenderNotifications(); // dialog thread messages #define TM_OPENDIALOG WM_USER @@ -49,11 +62,13 @@ DialogManager::DialogManager (Orbiter *orbiter, HWND hAppWnd) nList = nListBuf = 0; firstEntry = NULL; lastEntry = NULL; + InitImGui(); } DialogManager::~DialogManager () { Clear(); + ShutdownImGui(); } @@ -306,7 +321,7 @@ INT_PTR OrbiterDefDialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam case WM_SETCURSOR: // implements "focus follows mouse" behaviour if (!g_pOrbiter->StickyFocus() && g_pOrbiter->Cfg()->CfgUIPrm.MouseFocusMode == 2 && GetFocus() != hDlg && - !IsChild (hDlg, GetFocus()) && GetParent (hDlg) == g_pOrbiter->GetRenderWnd()) { + !IsChild (hDlg, GetFocus()) && GetParent (hDlg) == g_pOrbiter->GetRenderWnd()->Win32Handle()) { SetFocus (hDlg); return FALSE; } @@ -395,3 +410,638 @@ DWORD WINAPI DlgThreadProc (void *data) // ==================================================================== // End tread management // ==================================================================== +// +// ==================================================================== +// ImGui +// ==================================================================== + +DLLEXPORT ImGuiContext* GImGui = NULL; + +const ImWchar* GetGlyphRangesOrbiter() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x00A0, 0x02D9, // Polish characters + 0x0393, 0x03C2, // Greek characters + 0x221A, 0x221A, // √ + 0x222B, 0x222B, // ∫ + 0x2260, 0x2264, // ≠ ≤ ≥ + 0x02DD, 0x02DD, // Ë + 0, + }; + return &ranges[0]; +} + +// Styling adapted from https://gist.github.com/dougbinks/8089b4bbaccaaf6fa204236978d165a9 +static void ImGuiSetStyle(bool bStyleDark_, float alpha_) +{ + // Setup Dear ImGui style + ImGui::StyleColorsClassic(); + ImGui::StyleColorsLight(); + ImGuiStyle& style = ImGui::GetStyle(); + + style.Alpha = 1.0f; + style.FrameRounding = 3.0f; + style.WindowRounding = 3.0f; + style.ChildRounding = 3.0f; + style.PopupRounding = 3.0f; + style.ScrollbarRounding = 3.0f; + style.GrabRounding = 3.0f; + style.TabRounding = 3.0f; + style.WindowMenuButtonPosition = ImGuiDir_Right; + return; + // light style from Pacôme Danhiez (user itamago) https://github.com/ocornut/imgui/pull/511#issuecomment-175719267 + style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); + style.Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.94f); + style.Colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f); + style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f); + style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f); + style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f); + style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); + style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); + style.Colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); + style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); + style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); + style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); + style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); + style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 1.00f); + style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); + style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); + style.Colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); + style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); + style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); + style.Colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); + style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); + style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f); + style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); + style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); + style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); + style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); + + if( bStyleDark_ ) + { + for (int i = 0; i <= ImGuiCol_COUNT; i++) + { + ImVec4& col = style.Colors[i]; + float H, S, V; + ImGui::ColorConvertRGBtoHSV( col.x, col.y, col.z, H, S, V ); + + if( S < 0.1f ) + { + V = 1.0f - V; + } + ImGui::ColorConvertHSVtoRGB( H, S, V, col.x, col.y, col.z ); + if( col.w < 1.00f ) + { + col.w *= alpha_; + } + } + } + else + { + for (int i = 0; i <= ImGuiCol_COUNT; i++) + { + ImVec4& col = style.Colors[i]; + if( col.w < 1.00f ) + { + col.x *= alpha_; + col.y *= alpha_; + col.z *= alpha_; + col.w *= alpha_; + } + } + } +} + +void DialogManager::InitImGui() +{ + if(!gc) return; + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + // Viewports don't play nice when in full screen mode + if(!pOrbiter->IsFullscreen()) + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + //io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + ImGuiSetStyle(true, 1.0f); // Dark, alpha + + ImFontConfig config; + + static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + ImFontConfig icons_config; + icons_config.MergeMode = true; + icons_config.PixelSnapH = true; + icons_config.FontDataOwnedByAtlas = false; + + const CFG_FONTPRM &prm = g_pOrbiter->Cfg()->CfgFontPrm; + defaultFont = io.Fonts->AddFontFromFileTTF(prm.ImGui_FontFile, prm.ImGui_FontSize, &config, ImGui::GetIO().Fonts->GetGlyphRangesJapanese()); + io.Fonts->AddFontFromFileTTF("fa-solid-900.ttf", prm.ImGui_FontSize, &icons_config, icons_ranges); + monoFont = io.Fonts->AddFontFromFileTTF("Cousine-Regular.ttf", prm.ImGui_FontSize, &config, ImGui::GetIO().Fonts->GetGlyphRangesJapanese()); + io.Fonts->Build(); + + ImGui_ImplSDL3_InitForOther(g_pOrbiter->GetRenderWnd()->Inner()); + gc->clbkImGuiInit(); +} + +void DialogManager::ShutdownImGui() +{ + if(!gc) return; + + gc->clbkImGuiShutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); +} + +static bool EventIsKeyboard(Uint32 type) { + return type >= SDL_EVENT_KEY_UP && type < SDL_EVENT_MOUSE_MOTION; +} + +static bool EventIsMouse(Uint32 type) { + return type >= SDL_EVENT_MOUSE_MOTION && + type < SDL_EVENT_JOYSTICK_AXIS_MOTION; +} + +bool DialogManager::ConsumeEvent(const SDL_Event &event, bool &wantsOut) { + bool consumed = false; + ImGui_ImplSDL3_ProcessEvent(&event); + ImGuiIO &io = ImGui::GetIO(); + if ((io.WantCaptureMouse && EventIsMouse(event.type)) || + (io.WantCaptureKeyboard && EventIsKeyboard(event.type))) { + consumed = true; + } + // close requested are ignored TBD, until this doesn't use Win32 + + return consumed; +} + +void DialogManager::ImGuiNewFrame() +{ + if(!gc) return; + + gc->clbkImGuiNewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + //ImGui::ShowDemoWindow(); + + // Render notifications + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.f); // Round borders + // ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(43.f / 255.f, 43.f / 255.f, 43.f / 255.f, 240.f / 255.f)); // Background color + RenderNotifications(); // <-- Here we render all notifications + ImGui::PopStyleVar(1); // Don't forget to Pop() + // ImGui::PopStyleColor(1); + + // We can't use a range-based loop here because Show() may unregister the current dialog + for (auto it = DlgImGuiList.begin(); it != DlgImGuiList.end();) + { + auto current = it++; + if ((*current)->IsActive()) { + (*current)->Display(); + } + } + + ImGui::EndFrame(); +} + +ImFont *DialogManager::GetFont(ImGuiFont f) +{ + switch(f) { + case ImGuiFont::MONO: return monoFont; + case ImGuiFont::DEFAULT: return defaultFont; + default: return defaultFont; + } +} + +ImGuiDialog::~ImGuiDialog(){ + // Make sure this dialog is no longer referenced in the DialogManager + oapiCloseDialog(this); +} + +bool ImGuiDialog::HandleHelpButton() { + if(!helpfile.empty()) { + HELPCONTEXT hc; + hc.helpfile = const_cast(helpfile.c_str()); + hc.topic = helptopic.empty() ? NULL : const_cast(helptopic.c_str()); + hc.toc = (char*)"html/orbiter.chm::/orbiter.hhc"; + hc.index = (char*)"html/orbiter.chm::/orbiter.hhk"; + + if(ImGui::MenuButton(ICON_FA_CIRCLE_QUESTION, "Help")) { + g_pOrbiter->OpenHelp(&hc); + } + return true; + } + return false; +} + +void ImGuiDialog::Display() { + ImGui::SetNextWindowSize(ImVec2(defaultSize.width, defaultSize.height), ImGuiCond_FirstUseEver); + + if(ImGui::Begin(name.c_str(), &active)) { + HandleHelpButton(); + OnDraw(); + } + ImGui::End(); + if (!active) OnClose(); +} + +/* +Notification handling, borrowed heavily from https://github.com/patrickcjk/imgui-notify +Added: +- permanent discardable notifications +- copy text to clipboard (permanent notifications) +- deduplication +- fall animation +- limit notification box to the main viewport + +MIT License + +Copyright (c) 2021 Patrick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using namespace std::chrono_literals; +using duration_t = std::chrono::duration; +using time_point_t = std::chrono::time_point; + +const float NOTIFY_PADDING_X = 20.f; // Bottom-left X padding +const float NOTIFY_PADDING_Y = 20.f; // Bottom-left Y padding +const float NOTIFY_PADDING_MESSAGE_Y = 10.f; // Padding Y between each message +const uint32_t NOTIFICATION_FLAGS = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing; +const duration_t FADE_TIME = 0.5s; +static const ImVec4 notifcolors[4] = { + { 0, 255, 0, 255 }, // Success + { 255, 255, 0, 255 }, // Warning + { 255, 0, 0, 255 }, // Error + { 0, 157, 255, 255 } // Info +}; + +static const char *notificons[4] = { + ICON_FA_CIRCLE_CHECK, // Success + ICON_FA_TRIANGLE_EXCLAMATION, // Warning + ICON_FA_CIRCLE_EXCLAMATION, // Error + ICON_FA_CIRCLE_INFO // Info +}; + +static duration_t notifduration[4] = { + 3.0s, // Success + 10.0s, // Warning + 86400.0s, // Error - basically permanent and needs to be acknowledged + 7.0s // Info +}; + + +struct Notification +{ + uint32_t occurrences; + std::string title; + std::string content; + size_t hash; + ImVec4 color; + bool expired = false; + bool permanent = false; + const char *icon; + duration_t duration; + time_point_t creation; + float height; + float speed; + + Notification(uint32_t type, const char *title_, const char *content_, size_t hash_):title(title_),content(content_),hash(hash_) + { + if(type >= OAPINOTIF_INFO) type = OAPINOTIF_INFO; + color = notifcolors[type]; + icon = notificons[type]; + duration = notifduration[type]; + creation = std::chrono::steady_clock::now(); + occurrences = 1; + expired = false; + permanent = type == OAPINOTIF_ERROR; + speed = 1.0f; + height = -1.0; + } + void ReTrigger() { + occurrences++; + creation = std::chrono::steady_clock::now() - FADE_TIME; + duration = 86400.0s; + } + void UpdateState(time_point_t now, float h, duration_t dt) { + duration_t elapsed = now - creation; + // Update alpha/expiration base on elapsed time + if(elapsed > FADE_TIME + duration + FADE_TIME) { // expired + color.w = 0.0f; + expired = true; + } else if(elapsed < FADE_TIME) { // appearing + color.w = elapsed / FADE_TIME; + } else if(elapsed > FADE_TIME + duration) { // disappearing + color.w = 1.0f - (elapsed - FADE_TIME - duration) / FADE_TIME; + } else { // steady + color.w = 1.0; + } + + if(h < height) { + height -= speed; + speed += std::chrono::duration_cast(dt).count() * 0.001; + if(height <= h) { + height = h; + speed = 0.0f; + } + } else { + height = h; + speed = 0.0f; + } + } +}; + +static std::vector notifications; + +static void RenderNotifications() +{ + ImVec2 vp_size = ImGui::GetMainViewport()->Size; + vp_size.x += ImGui::GetMainViewport()->Pos.x; + vp_size.y += ImGui::GetMainViewport()->Pos.y; + float height = 0.0f; + + int i = 0; + static time_point_t last = std::chrono::steady_clock::now(); + auto now = std::chrono::steady_clock::now(); + duration_t dt = now - last; + for (auto ¬if: notifications) + { + notif.UpdateState(now, height, dt); + + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, notif.color.w); + ImGui::SetNextWindowPos(ImVec2(vp_size.x - NOTIFY_PADDING_X, vp_size.y - NOTIFY_PADDING_Y - notif.height), ImGuiCond_Always, ImVec2(1.0f, 1.0f)); + + // Generate new unique name for this toast + char window_name[32]; + sprintf(window_name, "##NOTIF%d", i); + i++; + ImGuiWindowFlags wf = NOTIFICATION_FLAGS; + if(notif.permanent) { + wf &= ~ImGuiWindowFlags_NoInputs; + } + + // Prevent the notification from spawning outside the main window + ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); + ImGui::Begin(window_name, NULL, wf); + + ImGui::PushTextWrapPos(vp_size.x / 2.0f); + + ImGui::PushStyleColor(ImGuiCol_Text, notif.color); + ImGui::TextUnformatted(notif.icon); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + if(notif.occurrences > 1) { + ImGui::Text("%s (x%d)", notif.title.c_str(), notif.occurrences); + } else { + ImGui::TextUnformatted(notif.title.c_str()); + } + + if(notif.permanent) + { + ImGui::SameLine(); + if (ImGui::SmallButton(ICON_FA_XMARK)) + { + // Change duration so that the notification will expire right now; + notif.duration = now - notif.creation - FADE_TIME; + } + } + + // In case ANYTHING was rendered in the top, we want to add a small padding so the text (or icon) looks centered vertically + if (!notif.content.empty()) + { + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5.0f); // Must be a better way to do this!!!! + ImGui::Separator(); + ImGui::TextUnformatted(notif.content.c_str()); // Render content text + + // Allow copying error messages to the clipboard + if(notif.permanent) { + char popup_name[32]; + sprintf(popup_name, "##POPUP%d", i); + + if (ImGui::BeginPopupContextItem(popup_name)) + { + if (ImGui::MenuItem("Copy")) { + ImGui::SetClipboardText(notif.content.c_str()); + } + ImGui::EndPopup(); + } + } + } + + ImGui::PopTextWrapPos(); + + ImGui::PopStyleVar(); + // Save height for next notification + height += ImGui::GetWindowHeight() + NOTIFY_PADDING_MESSAGE_Y; + + // End + ImGui::End(); + } + // Remove expired notifications + notifications.erase(std::remove_if(notifications.begin(), notifications.end(), + [=](auto ¬if){return notif.expired;}), + notifications.end()); + +} + + +// OAPI implementation +DLLEXPORT void oapiAddNotification(int type, const char *title, const char *content) { + const size_t hash = std::hash{}(title) ^ std::hash{}(content); + for (auto ¬if: notifications) { + if(notif.hash == hash && notif.title == title && notif.content == content) { + notif.ReTrigger(); + return; + } + } + notifications.emplace_back(type, title, content, hash); +} + +#include "imgui_internal.h" + +// ImGui utils +namespace ImGui { + // Resettable slider from https://github.com/ocornut/imgui/issues/1751 + DLLEXPORT bool SliderFloatReset(const char* label, float* v, float v_min, float v_max, float v_default, const char* display_format) + { + bool ret = ImGui::SliderFloat(label, v, v_min, v_max, display_format); + if (ImGui::BeginPopupContextItem(label)) + { + char buf[64]; + sprintf(buf, "Reset to %f", v_default); + if (ImGui::MenuItem(buf)) + *v = v_default; + ImGui::MenuItem("Close"); + ImGui::EndPopup(); + } + return ret; + } + + DLLEXPORT void PushFont(ImGuiFont f) + { + ImGui::PushFont(g_pOrbiter->DlgMgr()->GetFont(f)); + } + + // From imgui_demo.cpp, with added sameline argument + DLLEXPORT void HelpMarker(const char* desc, bool sameline) + { + if(sameline) + ImGui::SameLine(); + ImGui::TextDisabled(ICON_FA_CIRCLE_QUESTION); + if (ImGui::BeginItemTooltip()) + { + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + } + + // Code from thedmd: https://github.com/ocornut/imgui/issues/1496 + static ImVector s_GroupPanelLabelStack; + DLLEXPORT void BeginGroupPanel(const char* name, const ImVec2& size) + { + ImGui::BeginGroup(); + auto cursorPos = ImGui::GetCursorScreenPos(); + auto itemSpacing = ImGui::GetStyle().ItemSpacing; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + auto frameHeight = ImGui::GetFrameHeight(); + // workaround for incorrect capture of columns/table width by placing + // zero-sized dummy element in the same group, this ensure + // max X cursor position is updated correctly + ImGui::SameLine(0.0f, 0.0f); + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + ImGui::BeginGroup(); + ImVec2 effectiveSize = size; + if (size.x < 0.0f) + effectiveSize.x = ImGui::GetContentRegionAvail().x; + else + effectiveSize.x = size.x; + ImGui::Dummy(ImVec2(effectiveSize.x, 0.0f)); + ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f)); + ImGui::SameLine(0.0f, 0.0f); + ImGui::BeginGroup(); + ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f)); + ImGui::SameLine(0.0f, 0.0f); + ImGui::TextUnformatted(name); + auto labelMin = ImGui::GetItemRectMin(); + auto labelMax = ImGui::GetItemRectMax(); + ImGui::SameLine(0.0f, 0.0f); + ImGui::Dummy(ImVec2(0.0, frameHeight + itemSpacing.y)); + ImGui::BeginGroup(); + //ImGui::GetWindowDrawList()->AddRect(labelMin, labelMax, IM_COL32(255, 0, 255, 255)); + ImGui::PopStyleVar(2); + ImGui::GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f; + ImGui::GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f; + ImGui::GetCurrentWindow()->InnerRect.Max.x -= frameHeight * 0.5f; + ImGui::GetCurrentWindow()->Size.x -= frameHeight; + auto itemWidth = ImGui::CalcItemWidth(); + ImGui::PushItemWidth(ImMax(0.0f, itemWidth - frameHeight)); + s_GroupPanelLabelStack.push_back(ImRect(labelMin, labelMax)); + } + DLLEXPORT void EndGroupPanel() + { + ImGui::PopItemWidth(); + auto itemSpacing = ImGui::GetStyle().ItemSpacing; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + auto frameHeight = ImGui::GetFrameHeight(); + ImGui::EndGroup(); + //ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(0, 255, 0, 64), 4.0f); + ImGui::EndGroup(); + ImGui::SameLine(0.0f, 0.0f); + ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f)); + ImGui::Dummy(ImVec2(0.0, frameHeight - frameHeight * 0.5f - itemSpacing.y)); + ImGui::EndGroup(); + auto itemMin = ImGui::GetItemRectMin(); + auto itemMax = ImGui::GetItemRectMax(); + //ImGui::GetWindowDrawList()->AddRectFilled(itemMin, itemMax, IM_COL32(255, 0, 0, 64), 4.0f); + auto labelRect = s_GroupPanelLabelStack.back(); + s_GroupPanelLabelStack.pop_back(); + ImVec2 halfFrame = ImVec2(frameHeight * 0.25f, frameHeight) * 0.5f; + ImRect frameRect = ImRect(itemMin + halfFrame, itemMax - ImVec2(halfFrame.x, 0.0f)); + labelRect.Min.x -= itemSpacing.x; + labelRect.Max.x += itemSpacing.x; + for (int i = 0; i < 4; ++i) + { + switch (i) + { + // left half-plane + case 0: ImGui::PushClipRect(ImVec2(-FLT_MAX, -FLT_MAX), ImVec2(labelRect.Min.x, FLT_MAX), true); break; + // right half-plane + case 1: ImGui::PushClipRect(ImVec2(labelRect.Max.x, -FLT_MAX), ImVec2(FLT_MAX, FLT_MAX), true); break; + // top + case 2: ImGui::PushClipRect(ImVec2(labelRect.Min.x, -FLT_MAX), ImVec2(labelRect.Max.x, labelRect.Min.y), true); break; + // bottom + case 3: ImGui::PushClipRect(ImVec2(labelRect.Min.x, labelRect.Max.y), ImVec2(labelRect.Max.x, FLT_MAX), true); break; + } + ImGui::GetWindowDrawList()->AddRect( + frameRect.Min, frameRect.Max, + ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)), + halfFrame.x); + ImGui::PopClipRect(); + } + ImGui::PopStyleVar(2); + ImGui::GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f; + ImGui::GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f; + ImGui::GetCurrentWindow()->InnerRect.Max.x += frameHeight * 0.5f; + ImGui::GetCurrentWindow()->Size.x += frameHeight; + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + ImGui::EndGroup(); + //ImGui::Dummy(ImVec2(0.0f, 0.0f)); + } + + DLLEXPORT bool MenuButton(const char *label, const char *tooltip, float xoffset) + { + ImGuiContext& g = *GImGui; + const ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImRect titleBarRect = window->TitleBarRect(); + const ImGuiStyle& style = g.Style; + ImGuiLastItemData last_item_backup = g.LastItemData; + ImGui::PushClipRect( titleBarRect.Min, titleBarRect.Max, false ); + float width = (strlen(label)+1) * g.FontSize; + float offset = titleBarRect.Max.x - width - titleBarRect.Min.x - g.Style.FramePadding.x; + offset -= xoffset; + ImGui::SetCursorPos( ImVec2( offset, 0.0f ) ); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); + bool ret = ImGui::Button( label ); + ImGui::PopStyleColor(); + if(tooltip && ImGui::IsItemHovered()) + ImGui::SetTooltip(tooltip); + ImGui::PopClipRect(); + g.LastItemData = last_item_backup; + return ret; + } +} diff --git a/Src/Orbiter/DlgMgr.h b/Src/Orbiter/DlgMgr.h index 697c67661..45b50c1a4 100644 --- a/Src/Orbiter/DlgMgr.h +++ b/Src/Orbiter/DlgMgr.h @@ -9,6 +9,10 @@ #include "DialogWin.h" #include "Orbiter.h" +#include +#include "imgui.h" +#include "imgui_extras.h" +class ImGuiDialog; class oapi::GraphicsClient; extern Orbiter *g_pOrbiter; @@ -29,7 +33,7 @@ class DialogManager { // Make sure that a dialog of type DlgType is open and return a pointer to it. // This opens the dialog if not yet present. // Use this function for dialogs that should only have a single instance - template + template, bool> = true> DlgType *EnsureEntry (HINSTANCE hInst = 0, void *context = 0) { if (!hInst) hInst = g_pOrbiter->GetInstance(); @@ -41,11 +45,11 @@ class DialogManager { // Create a new instance of dialog type DlgType and return a pointer to it. // This opens a new dialog, even if one of this type was open already. // Use this function for dialogs that can have multiple instances. - template + template, bool> = true> DlgType *MakeEntry (HINSTANCE hInst = 0, void *context = 0) { if (!hInst) hInst = g_pOrbiter->GetInstance(); - HWND hParent = g_pOrbiter->GetRenderWnd(); + HWND hParent = g_pOrbiter->GetRenderWnd()->Win32Handle(); DlgType *pDlg = new DlgType (hInst, hParent, context); AddEntry (pDlg); return pDlg; @@ -53,7 +57,7 @@ class DialogManager { // Returns a pointer to the first instance of dialog type DlgType, // or 0 if no instance exists. - template + template, bool> = true> DlgType *EntryExists (HINSTANCE hInst) { DIALOGENTRY *tmp = firstEntry; @@ -149,6 +153,81 @@ class DialogManager { // End tread management // ==================================================================== + + // ==================================================================== + // ImGui management + // ==================================================================== + std::list DlgImGuiList; +public: + // Make sure that a dialog of type DlgType is open and return a pointer to it. + // This opens the dialog if not yet present. + // Use this function for dialogs that should only have a single instance + template, bool> = true> + T* EnsureEntry() + { + T* dlg = EntryExists(); + if(!dlg) + dlg = MakeEntry(); + if(dlg) + dlg->Activate(); + return dlg; + } + + // Create a new instance of dialog type DlgType and return a pointer to it. + // This opens a new dialog, even if one of this type was open already. + // Use this function for dialogs that can have multiple instances. + template, bool> = true> + T* MakeEntry() + { + T* pDlg = new T(); + AddEntry(pDlg); + return pDlg; + } + + // Returns a pointer to the first instance of dialog type DlgType, + // or 0 if no instance exists. + template, bool> = true> + T* EntryExists() + { + for (auto& e : DlgImGuiList) { + T *dlg = dynamic_cast(e); + if(dlg) return dlg; + } + return nullptr; + } + + void AddEntry(ImGuiDialog* dlg) + { + for (auto& e : DlgImGuiList) { + if (e == dlg) { + return; + } + } + DlgImGuiList.push_back(dlg); + } + + bool DelEntry(ImGuiDialog* dlg) + { + for (auto it = DlgImGuiList.begin(); it != DlgImGuiList.end(); ) { + if (*it == dlg) { + it = DlgImGuiList.erase(it); + return true; + } + else { + ++it; + } + } + return false; + } + + void ImGuiNewFrame(); + ImFont *GetFont(ImGuiFont f); + bool ConsumeEvent(const SDL_Event &event, bool &wantsOut); +private: + void InitImGui(); + void ShutdownImGui(); + ImFont *defaultFont; + ImFont *monoFont; }; INT_PTR OrbiterDefDialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/Src/Orbiter/DlgRecorder.cpp b/Src/Orbiter/DlgRecorder.cpp index 068912f47..78b365421 100644 --- a/Src/Orbiter/DlgRecorder.cpp +++ b/Src/Orbiter/DlgRecorder.cpp @@ -5,207 +5,112 @@ // Flight recorder dialog // ====================================================================== -#define STRICT 1 - #include "DlgRecorder.h" #include "Orbiter.h" -#include "Resource.h" -#include "Resource2.h" +#include "OrbiterAPI.h" +#include "imgui.h" +#include "IconsFontAwesome6.h" -extern Orbiter *g_pOrbiter; extern Vessel *g_focusobj; -extern HELPCONTEXT DefHelpContext; - -static int RecItemEx[4] = {IDC_REC_GRPADVANCED, IDC_REC_WARP, IDC_REC_SYSSAMPLE, IDC_REC_RECFOCUS}; -static int RecItemStd[3] = {IDC_REC_ROLLDOWN, IDC_REC_HELP1, IDC_REC_CANCEL1}; - -// ====================================================================== - -DlgRecorder::DlgRecorder (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_RECPLAY, 0, 0, context) -{ -} - -// ====================================================================== - -void DlgRecorder::SetupDialog (HWND hDlg) -{ - static int RecItem[4] = {IDC_REC_GRPRECORD, IDC_REC_SCNLABEL, IDC_REC_SCENARIO, IDC_REC_ROLLDOWN}; - static int RecItem2[3] = {IDC_REC_SCNLABEL, IDC_REC_SCENARIO, IDC_REC_SYSSAMPLE}; - static int PlayItem[6] = {IDC_REC_GRPREPLAY, IDC_REC_SHOWNOTES, IDC_REC_PLAYRECSPEED, IDC_REC_USECAM, IDC_REC_USEFOCUS, IDC_REC_EDITOR}; - static const char *statestr[3] = {"Status:\nNormal", "Status:\nRecording", "Status:\nPlaying"}; - - int i, status = g_pOrbiter->RecorderStatus(); - - for (i = 0; i < 3; i++) - EnableWindow (GetDlgItem (hDlg, RecItem2[i]), status != 1); - if (status == 2) { - for (i = 0; i < 4; i++) ShowWindow (GetDlgItem (hDlg, RecItem[i]), FALSE); - for (i = 0; i < 6; i++) ShowWindow (GetDlgItem (hDlg, PlayItem[i]), TRUE); - } else { - for (i = 0; i < 6; i++) ShowWindow (GetDlgItem (hDlg, PlayItem[i]), FALSE); - for (i = 0; i < 4; i++) ShowWindow (GetDlgItem (hDlg, RecItem[i]), TRUE); - ShowWindow (GetDlgItem (hDlg, IDC_REC_ROLLDOWN), IsWindowVisible (GetDlgItem (hDlg, IDC_REC_GRPADVANCED)) ? FALSE:TRUE); - } - - int img = (status == 0 ? IDI_REC_ICON : IDI_STOP_ICON); - SendDlgItemMessage (hDlg, IDC_REC_CTRL, BM_SETIMAGE, IMAGE_ICON, - (LPARAM)LoadImage (g_pOrbiter->GetInstance(), MAKEINTRESOURCE(img), IMAGE_ICON, 32, 32, LR_SHARED)); - SetWindowText (GetDlgItem (hDlg, IDC_REC_STATUS), statestr[status]); - SendDlgItemMessage (hDlg, IDC_REC_WARP, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bRecordWarp ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_PLAYRECSPEED, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayWarp ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_RECFOCUS, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bRecordFocus ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_USEFOCUS, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayFocus ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_USECAM, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayCam ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_SYSSAMPLE, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bSysInterval ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_SHOWNOTES, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.bShowNotes ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_ATTECL, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordAttFrame == 0 ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_ATTHOR, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordAttFrame == 1 ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_POSECL, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordPosFrame == 0 ? BST_CHECKED:BST_UNCHECKED, 0); - SendDlgItemMessage (hDlg, IDC_REC_POSEQU, BM_SETCHECK, g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordPosFrame == 1 ? BST_CHECKED:BST_UNCHECKED, 0); -} - -// ====================================================================== - -void DlgRecorder::GetRecordName (char *str, int maxlen) const -{ - GetWindowText (GetDlgItem (hWnd, IDC_REC_SCENARIO), str, maxlen); -} - -// ====================================================================== +extern Orbiter *g_pOrbiter; -void DlgRecorder::ShowAdvancedRec (HWND hDlg) -{ - int i; - SetWindowPos (hDlg, HWND_TOP, 0, 0, RecorderDlg_w, RecorderDlg_h2, SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); - for (i = 0; i < 3; i++) - ShowWindow (GetDlgItem (hDlg, RecItemStd[i]), SW_HIDE); - for (i = 0; i < 4; i++) - ShowWindow (GetDlgItem (hDlg, RecItemEx[i]), SW_SHOW); +DlgRecorder::DlgRecorder() : ImGuiDialog(ICON_FA_FILM " Flight recorder/player", {329,354}) { + SetHelp("html/orbiter.chm", "/recorder.htm"); + strcpy(m_ScenarioFile, "test_record"); } -// ====================================================================== +void DlgRecorder::OnDraw() { + int status = g_pOrbiter->RecorderStatus(); + static const char *statestr[3] = {"Status: Normal", "Status: Recording", "Status: Playing"}; + ImGui::TextUnformatted(statestr[status]); -void DlgRecorder::HideAdvancedRec (HWND hDlg) -{ - int i; - SetWindowPos (hDlg, HWND_TOP, 0, 0, RecorderDlg_w, RecorderDlg_h1, SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); - for (i = 0; i < 4; i++) - ShowWindow (GetDlgItem (hDlg, RecItemEx[i]), SW_HIDE); - for (i = 0; i < 3; i++) - ShowWindow (GetDlgItem (hDlg, RecItemStd[i]), SW_SHOW); + switch(status) { + case 0: DrawNormalRecording(false); break; + case 1: DrawNormalRecording(true); break; + case 2: DrawPlaying(); break; + } } -// ====================================================================== - -BOOL DlgRecorder::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - RECT r1, r2; - GetWindowRect (hDlg, &r1); - GetWindowRect (GetDlgItem (hDlg, IDC_REC_ROLLDOWN), &r2); - RecorderDlg_w = r1.right-r1.left; - RecorderDlg_h2 = r1.bottom-r1.top; - RecorderDlg_h1 = r2.bottom-r1.top + ((r2.bottom-r2.top)*2)/3; - HideAdvancedRec (hDlg); - SetupDialog (hDlg); - SetWindowText (GetDlgItem (hDlg, IDC_REC_SCENARIO), g_pOrbiter->GetDefRecordName()); - SendDlgItemMessage (hDlg, IDC_REC_ROLLDOWN, BM_SETIMAGE, IMAGE_BITMAP, - (LPARAM)LoadImage (hInst, MAKEINTRESOURCE(IDB_DNARROW), IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADTRANSPARENT|LR_LOADMAP3DCOLORS)); - SendDlgItemMessage (hDlg, IDC_REC_ROLLUP, BM_SETIMAGE, IMAGE_BITMAP, - (LPARAM)LoadImage (hInst, MAKEINTRESOURCE(IDB_UPARROW), IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADTRANSPARENT|LR_LOADMAP3DCOLORS)); - - return TRUE; +void DlgRecorder::DrawNormalRecording(bool recording) { + if(recording) { + if(ImGui::Button("STOP")) { + g_pOrbiter->ToggleRecorder (); + } + } else { + if(ImGui::Button("REC")) { + if(!g_pOrbiter->ToggleRecorder ()) + ImGui::OpenPopup("Warning"); + } + } + + bool unused_open = true; + if (ImGui::BeginPopupModal("Warning", &unused_open)) + { + ImGui::TextUnformatted("A flight record under this name already exists!"); + if(ImGui::Button("Overwrite")) { + g_pOrbiter->ToggleRecorder (true); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if(ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::Separator(); + ImGui::InputText("Scenario", m_ScenarioFile, sizeof(m_ScenarioFile)); + if(ImGui::CollapsingHeader("Advanced")) { + ImGui::Checkbox("Record Time Acceleration", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bRecordWarp); + ImGui::Checkbox("Record Focus Events", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bRecordFocus); + ImGui::BeginDisabled(recording); + ImGui::Checkbox("Sampling in System Time Steps", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bSysInterval); + //char buf[20]=""; + //ImGui::InputText("sec. sampling", buf, 20); + ImGui::Separator(); + ImGui::TextUnformatted("Attitude Data"); + ImGui::RadioButton("Ecliptic Frame###ef1", &g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordAttFrame, 0); + ImGui::RadioButton("Local Horizon Frame", &g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordAttFrame, 1); + + ImGui::Separator(); + ImGui::TextUnformatted("Position / Velocity Data"); + ImGui::RadioButton("Ecliptic Frame###ef2", &g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordPosFrame, 0); + ImGui::RadioButton("Equatorial Frame", &g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordPosFrame, 1); + ImGui::EndDisabled(); + } } -// ====================================================================== - -BOOL DlgRecorder::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDC_REC_CTRL: - switch (g_pOrbiter->RecorderStatus()) { - case 0: - case 1: - g_pOrbiter->ToggleRecorder (); - break; - case 2: - g_pOrbiter->EndPlayback(); - break; - } - break; - case IDC_REC_ROLLDOWN: - ShowAdvancedRec (hDlg); - break; - case IDC_REC_ROLLUP: - HideAdvancedRec (hDlg); - break; - case IDC_REC_EDITOR: - g_pOrbiter->FRecorder_ToggleEditor(); - break; - case IDC_REC_WARP: - g_pOrbiter->Cfg()->CfgRecPlayPrm.bRecordWarp = (SendDlgItemMessage (hDlg, IDC_REC_WARP, BM_GETCHECK, 0, 0) == BST_CHECKED); - break; - case IDC_REC_PLAYRECSPEED: - g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayWarp = (SendDlgItemMessage (hDlg, IDC_REC_PLAYRECSPEED, BM_GETCHECK, 0, 0) == BST_CHECKED); +void DlgRecorder::DrawPlaying() { + if(ImGui::Button("STOP")) { + g_pOrbiter->EndPlayback (); + } + ImGui::Separator(); + ImGui::TextUnformatted("Replay Options"); + ImGui::Checkbox("Show Inflight Notes", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bShowNotes); + if(ImGui::Checkbox("Play at Recording Speed", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayWarp)) { if (g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayWarp) { extern double RecordingSpeed; g_pOrbiter->SetWarpFactor (RecordingSpeed, true); } - break; - case IDC_REC_USEFOCUS: - g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayFocus = (SendDlgItemMessage (hDlg, IDC_REC_USEFOCUS, BM_GETCHECK, 0, 0) == BST_CHECKED); + } + if(ImGui::Checkbox("Use Recorded Camera Settings", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayCam)) { + if (g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayCam) { + // should set camera according to current playback status here! + } + } + if(ImGui::Checkbox("Use Recorded Focus Events", &g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayFocus)) { if (g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayFocus) { extern Vessel *vfocus; if (vfocus && vfocus != g_focusobj) g_pOrbiter->SetFocusObject (vfocus); // do we need to check if vfocus is valid? } - break; - case IDC_REC_USECAM: - g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayCam = (SendDlgItemMessage (hDlg, IDC_REC_USECAM, BM_GETCHECK, 0, 0) == BST_CHECKED); - if (g_pOrbiter->Cfg()->CfgRecPlayPrm.bReplayCam) { - // should set camera according to current playback status here! - } - break; - case IDC_REC_SYSSAMPLE: - g_pOrbiter->Cfg()->CfgRecPlayPrm.bSysInterval = (SendDlgItemMessage (hDlg, IDC_REC_SYSSAMPLE, BM_GETCHECK, 0, 0) == BST_CHECKED); - break; - case IDC_REC_SHOWNOTES: - g_pOrbiter->Cfg()->CfgRecPlayPrm.bShowNotes = (SendDlgItemMessage (hDlg, IDC_REC_SHOWNOTES, BM_GETCHECK, 0, 0) == BST_CHECKED); - break; - case IDC_REC_ATTECL: - if (SendDlgItemMessage (hDlg, IDC_REC_ATTECL, BM_GETCHECK, 0, 0) == BST_CHECKED) - g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordAttFrame = 0; - break; - case IDC_REC_ATTHOR: - if (SendDlgItemMessage (hDlg, IDC_REC_ATTHOR, BM_GETCHECK, 0, 0) == BST_CHECKED) - g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordAttFrame = 1; - break; - case IDC_REC_POSECL: - if (SendDlgItemMessage (hDlg, IDC_REC_POSECL, BM_GETCHECK, 0, 0) == BST_CHECKED) - g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordPosFrame = 0; - break; - case IDC_REC_POSEQU: - if (SendDlgItemMessage (hDlg, IDC_REC_POSEQU, BM_GETCHECK, 0, 0) == BST_CHECKED) - g_pOrbiter->Cfg()->CfgRecPlayPrm.RecordPosFrame = 1; - break; - case IDC_REC_HELP1: - case IDC_REC_HELP2: - DefHelpContext.topic = (char*)"/recorder.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDCANCEL: - case IDC_REC_CANCEL1: - case IDC_REC_CANCEL2: - g_pOrbiter->CloseDialog (hDlg); - break; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); + } + if(ImGui::Button("Editor")) { + g_pOrbiter->FRecorder_ToggleEditor(); + } } -// ====================================================================== - -BOOL DlgRecorder::OnUser1 (HWND hDlg, WPARAM wParam, LPARAM lParam) +void DlgRecorder::GetRecordName (char *str, int maxlen) const { - SetupDialog (hDlg); - return TRUE; + strncpy(str, m_ScenarioFile, maxlen); } diff --git a/Src/Orbiter/DlgRecorder.h b/Src/Orbiter/DlgRecorder.h index 30d66c638..5b6a12252 100644 --- a/Src/Orbiter/DlgRecorder.h +++ b/Src/Orbiter/DlgRecorder.h @@ -8,23 +8,17 @@ #ifndef __DLGRECORDER_H #define __DLGRECORDER_H -#include "DialogWin.h" +#include "OrbiterAPI.h" -class DlgRecorder: public DialogWin { +class DlgRecorder : public ImGuiDialog { public: - DlgRecorder (HINSTANCE hInstance, HWND hParent, void *context); - BOOL OnInitDialog (HWND hWnd, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hWnd, WORD id, WORD code, HWND hControl); - BOOL OnUser1 (HWND hWnd, WPARAM wParam, LPARAM lParam); - void GetRecordName (char *str, int maxlen) const; + DlgRecorder(); + void OnDraw() override; + void DrawNormalRecording(bool recording); + void DrawPlaying(); -protected: - void SetupDialog (HWND hDlg); - void ShowAdvancedRec (HWND hDlg); - void HideAdvancedRec (HWND hDlg); - -private: - int RecorderDlg_w, RecorderDlg_h1, RecorderDlg_h2; + char m_ScenarioFile[128]; + void GetRecordName (char *str, int maxlen) const; }; #endif // !__DLGRECORDER_H \ No newline at end of file diff --git a/Src/Orbiter/DlgTacc.cpp b/Src/Orbiter/DlgTacc.cpp index 16b63ebcb..e2a3559ce 100644 --- a/Src/Orbiter/DlgTacc.cpp +++ b/Src/Orbiter/DlgTacc.cpp @@ -5,123 +5,55 @@ // Time acceleration dialog // ====================================================================== -#define STRICT 1 - #include "DlgTacc.h" #include "Orbiter.h" -#include "resource.h" -#include "resource2.h" +#include "imgui.h" +#include "IconsFontAwesome6.h" -extern Orbiter *g_pOrbiter; extern TimeData td; -extern HELPCONTEXT DefHelpContext; - -// ====================================================================== - -DlgTacc::DlgTacc (HINSTANCE hInstance, HWND hParent, void *context) -: DialogWin (hInstance, hParent, IDD_TIMEWARP, 0, 0, context) -{ - pos = &g_pOrbiter->Cfg()->CfgWindowPos.DlgTacc; -} - -// ====================================================================== - -void DlgTacc::Message (DWORD msg, void *data) -{ - bool paused = (data != 0); - SetWindowText (GetDlgItem (hWnd, IDC_WARP_PAUSE), paused ? "Resume" : "Pause"); -} - -// ====================================================================== - -void DlgTacc::RegisterWarp (HWND hDlg, double warp, bool commit, bool edit, bool slide) -{ - if (commit) g_pOrbiter->SetWarpFactor (warp); - if (slide) { - int sliderpos; - if (warp <= 1.0) sliderpos = (int)(warp*10.0); - else if (warp <= 10.0) sliderpos = (int)(warp+9.0); - else if (warp <= 100.0) sliderpos = (int)(0.1*warp+18.0); - else sliderpos = (int)(0.01*warp+27.0); - SendDlgItemMessage (hDlg, IDC_WARP_SLIDER, TBM_SETPOS, TRUE, (LONG)sliderpos); - } - if (edit) { - char cbuf[256]; - double pwarp; - GetWindowText (GetDlgItem (hDlg, IDC_WARP_EDIT), cbuf, 256); - int res = sscanf (cbuf, "%lf", &pwarp); - if (res != 1 || fabs (warp-pwarp) > 1e-8) { - sprintf (cbuf, "%0.1f", warp); - SetWindowText (GetDlgItem (hDlg, IDC_WARP_EDIT), cbuf); - } - } -} - -// ====================================================================== - -BOOL DlgTacc::OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam) -{ - SendDlgItemMessage (hDlg, IDC_WARP_SLIDER, TBM_SETRANGE, FALSE, MAKELONG(1,37)); - SendDlgItemMessage (hDlg, IDC_WARP_SLIDER, TBM_SETPAGESIZE, 0, 9L); - SendDlgItemMessage (hDlg, IDC_WARP_SLIDER, TBM_SETTICFREQ, 9, 0); - RegisterWarp (hDlg, td.Warp(), false); - SetWindowText (GetDlgItem (hDlg, IDC_WARP_PAUSE), g_pOrbiter->IsRunning() ? "Pause": "Resume"); - return TRUE; -} - -// ====================================================================== +extern Orbiter *g_pOrbiter; -BOOL DlgTacc::OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl) -{ - switch (id) { - case IDHELP: - DefHelpContext.topic = (char*)"/timeacc.htm"; - g_pOrbiter->OpenHelp (&DefHelpContext); - return TRUE; - case IDC_WARP_01: - RegisterWarp (hDlg, 0.1); - return TRUE; - case IDC_WARP_1: - RegisterWarp (hDlg, 1.0); - return TRUE; - case IDC_WARP_10: - RegisterWarp (hDlg, 10.0); - return TRUE; - case IDC_WARP_100: - RegisterWarp (hDlg, 100.0); - return TRUE; - case IDC_WARP_1000: - RegisterWarp (hDlg, 1000.0); - return TRUE; - case IDC_WARP_EDIT: - if (code == EN_UPDATE) { - double warp; - char cbuf[256]; - GetWindowText (hControl, cbuf, 256); - warp = atof (cbuf); - if (warp >= 0.1 && warp <= 1e5) - RegisterWarp (hDlg, warp, true, false); - return TRUE; - } - break; - case IDC_WARP_PAUSE: - g_pOrbiter->TogglePause(); - return TRUE; - } - return DialogWin::OnCommand (hDlg, id, code, hControl); +DlgTacc::DlgTacc() : ImGuiDialog(ICON_FA_CLOCK " Orbiter: Time acceleration",{357,135}) { + SetHelp("html/orbiter.chm", "/timeacc.htm"); } -// ====================================================================== +void DlgTacc::OnDraw() { + const ImVec2 button_sz(ImVec2(50, 20)); + + if(ImGui::Button("0.1x", button_sz)) { + g_pOrbiter->SetWarpFactor (0.1); + } + ImGui::SameLine(); + if(ImGui::Button("1x", button_sz)) { + g_pOrbiter->SetWarpFactor (1.0); + } + ImGui::SameLine(); + if(ImGui::Button("10x", button_sz)) { + g_pOrbiter->SetWarpFactor (10.0); + } + ImGui::SameLine(); + if(ImGui::Button("100x", button_sz)) { + g_pOrbiter->SetWarpFactor (100.0); + } + ImGui::SameLine(); + if(ImGui::Button("1000x", button_sz)) { + g_pOrbiter->SetWarpFactor (1000.0); + } + ImGui::SameLine(); + if(ImGui::Button("10000x", button_sz)) { + g_pOrbiter->SetWarpFactor (10000.0); + } + + float warp = td.Warp(); + ImGui::SetNextItemWidth(-FLT_MIN); + if(ImGui::SliderFloat("##slider warp", &warp, 0.1f, 10000.0f, "%.1f", ImGuiSliderFlags_Logarithmic)) { + g_pOrbiter->SetWarpFactor (warp); + } + + ImGui::NewLine(); + + ImGui::SetCursorPosX((ImGui::GetWindowSize().x - button_sz.x) * 0.5f); + if(ImGui::Button(g_pOrbiter->IsRunning()?"Pause":"Resume", button_sz)) + g_pOrbiter->TogglePause(); -BOOL DlgTacc::OnHScroll (HWND hDlg, WORD request, WORD curpos, HWND hControl) -{ - if (request == TB_THUMBTRACK) { - int sliderpos = SendDlgItemMessage (hDlg, IDC_WARP_SLIDER, TBM_GETPOS, 0, 0); - if (sliderpos <= 10) RegisterWarp (hDlg, sliderpos*0.1); - else if (sliderpos <= 19) RegisterWarp (hDlg, sliderpos-9); - else if (sliderpos <= 28) RegisterWarp (hDlg, (sliderpos-18)*10); - else RegisterWarp (hDlg, (sliderpos-27)*100); - return TRUE; - } - return DialogWin::OnHScroll (hDlg, request, curpos, hControl); } diff --git a/Src/Orbiter/DlgTacc.h b/Src/Orbiter/DlgTacc.h index efaf15bd5..2e025ec74 100644 --- a/Src/Orbiter/DlgTacc.h +++ b/Src/Orbiter/DlgTacc.h @@ -8,16 +8,12 @@ #ifndef __DLGTACC_H #define __DLGTACC_H -#include "DialogWin.h" +#include "OrbiterAPI.h" -class DlgTacc: public DialogWin { +class DlgTacc : public ImGuiDialog { public: - DlgTacc (HINSTANCE hInstance, HWND hParent, void *context); - void Message (DWORD msg, void *data); - void RegisterWarp (HWND hDlg, double warp, bool commit = true, bool edit = true, bool slide = true); - BOOL OnInitDialog (HWND hDlg, WPARAM wParam, LPARAM lParam); - BOOL OnCommand (HWND hDlg, WORD id, WORD code, HWND hControl); - BOOL OnHScroll (HWND hDlg, WORD request, WORD curpos, HWND hControl); + DlgTacc(); + void OnDraw() override; }; #endif // !__DLGTACC_H \ No newline at end of file diff --git a/Src/Orbiter/FlightRecorder.cpp b/Src/Orbiter/FlightRecorder.cpp index 96c8ffc3c..7439c3e43 100644 --- a/Src/Orbiter/FlightRecorder.cpp +++ b/Src/Orbiter/FlightRecorder.cpp @@ -15,6 +15,7 @@ #include "Pane.h" #include "State.h" #include "MenuInfoBar.h" +#include "DlgMgr.h" #include #include #include @@ -922,7 +923,7 @@ void Orbiter::FRecorder_Play () g_pOrbiter->Timejump(tgtmjd, PROP_ORBITAL_FIXEDSURF); } } else if (!_strnicmp (s, "ENDSESSION", 10)) { - if (hRenderWnd) PostMessage (hRenderWnd, WM_CLOSE, 0, 0); + if (hRenderWnd) PostMessage (hRenderWnd->Win32Handle(), WM_CLOSE, 0, 0); } } *FRsys_stream >> frec_sys_simt; // read time for next event @@ -934,14 +935,10 @@ void Orbiter::FRecorder_Play () } void Orbiter::FRecorder_ToggleEditor () -{ - if (FReditor) { - delete FReditor; - FReditor = 0; - } else { - const char *playbackdir = pState->PlaybackDir(); - FReditor = new PlaybackEditor (this, playbackdir); TRACENEW - } +{ + DlgPlaybackEditor *m_DlgPlaybackEditor = g_pOrbiter->DlgMgr()->EnsureEntry(); + const char *playbackdir = pState->PlaybackDir(); + m_DlgPlaybackEditor->Load(playbackdir); } // ================================================================ diff --git a/Src/Orbiter/GraphicsAPI.cpp b/Src/Orbiter/GraphicsAPI.cpp index 4681cfbe6..f9b862b5d 100644 --- a/Src/Orbiter/GraphicsAPI.cpp +++ b/Src/Orbiter/GraphicsAPI.cpp @@ -4,20 +4,22 @@ #define STRICT 1 #define OAPI_IMPLEMENTATION -#include "Orbiter.h" -#include "Launchpad.h" -#include "LpadTab.h" -#include "TabVideo.h" -#include "Psys.h" -#include "Pane.h" -#include "VCockpit.h" #include "GraphicsAPI.h" #include "DlgMgr.h" +#include "Launchpad.h" #include "Log.h" +#include "LpadTab.h" +#include "Orbiter.h" +#include "Pane.h" +#include "Psys.h" +#include "TabVideo.h" #include "Util.h" +#include "VCockpit.h" #include "resource.h" -#include + +#include #include +#include namespace fs = std::filesystem; using std::min; @@ -41,7 +43,6 @@ OAPIFUNC INT_PTR CALLBACK LaunchpadVideoWndProc (HWND hWnd, UINT uMsg, WPARAM wP GraphicsClient::GraphicsClient (HINSTANCE hInstance): Module (hInstance) { - hOrbiterInst = g_pOrbiter->GetInstance(); VideoData.fullscreen = false; VideoData.forceenum = true; VideoData.trystencil = false; @@ -81,12 +82,12 @@ GraphicsClient::~GraphicsClient () bool GraphicsClient::clbkInitialise () { // Register a window class for the render window - WNDCLASS wndClass = {0, ::WndProc, 0, 0, hModule, - LoadIcon (g_pOrbiter->GetInstance(), MAKEINTRESOURCE(IDI_MAIN_ICON)), - LoadCursor (NULL, IDC_ARROW), - (HBRUSH)GetStockObject (WHITE_BRUSH), - NULL, strWndClass}; - RegisterClass (&wndClass); + // WNDCLASS wndClass = {0, ::WndProc, 0, 0, hModule, + // LoadIcon (g_pOrbiter->GetInstance(), MAKEINTRESOURCE(IDI_MAIN_ICON)), + // LoadCursor (NULL, IDC_ARROW), + // (HBRUSH)GetStockObject (WHITE_BRUSH), + // NULL, strWndClass}; + // RegisterClass (&wndClass); if (clbkUseLaunchpadVideoTab() && g_pOrbiter->Launchpad()) { hVid = g_pOrbiter->Launchpad()->GetTab(PG_VID)->TabWnd(); @@ -268,20 +269,17 @@ const void *GraphicsClient::GetConfigParam (DWORD paramtype) const // ====================================================================== -HWND GraphicsClient::clbkCreateRenderWindow () -{ - HWND hWnd; +std::shared_ptr GraphicsClient::clbkCreateRenderWindow() { + std::shared_ptr window; - if (VideoData.fullscreen) { - hWnd = CreateWindow (strWndClass, "", // dummy window - WS_POPUP | WS_EX_TOPMOST| WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, 0, 0, hModule, (LPVOID)this); - } else { - hWnd = CreateWindow (strWndClass, "", - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, VideoData.winw, VideoData.winh, 0, 0, hModule, (LPVOID)this); - } - return hWnd; + if (VideoData.fullscreen) { + window = std::make_shared("", 10, 10, + SDL_WINDOW_FULLSCREEN); + } else { + window = std::make_shared("", VideoData.winw, + VideoData.winh, 0); + } + return window; } // ====================================================================== @@ -645,40 +643,25 @@ bool GraphicsClient::clbkCopyBitmap (SURFHANDLE pdds, HBITMAP hbm, // ====================================================================== -HWND GraphicsClient::InitRenderWnd (HWND hWnd) -{ - if (!hWnd) { // create a dummy window - hWnd = CreateWindow (strWndClass, "", - WS_POPUP | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, 0, 0, hModule, 0); - } - SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR)this); - // store class instance with window for access in the message handler - - char title[256], cbuf[128]; - extern const TCHAR *g_strAppTitle; - strcpy (title, g_strAppTitle); - GetWindowText (hWnd, cbuf, 128); - if (cbuf[0]) { - strcat (title, " "); - strcat (title, cbuf); - } - SetWindowText (hWnd, title); - hRenderWnd = hWnd; - return hRenderWnd; + +extern const TCHAR* g_strAppTitle; + +void GraphicsClient::InitRenderWnd( + std::shared_ptr &window) { + if (!window) { + window = std::make_shared("", 10, 10, 0); + } + + auto title = std::string(g_strAppTitle) + " " + + SDL_GetWindowTitle(window->Inner()); + SDL_SetWindowTitle(window->Inner(), title.c_str()); } // ====================================================================== - -LRESULT GraphicsClient::RenderWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - // graphics-specific stuff to go here - default: - return g_pOrbiter->MsgProc (hWnd, uMsg, wParam, lParam); - } - return DefWindowProc (hWnd, uMsg, wParam, lParam); +bool GraphicsClient::RenderWndProc(const SDL_Event &event, + bool &wantsOut) { + return g_pOrbiter->MsgProc(event, wantsOut); } // ====================================================================== @@ -883,18 +866,6 @@ void ScreenAnnotation::Render () // ====================================================================== // Nonmember functions -//----------------------------------------------------------------------- -// Name: WndProc() -// Desc: Static msg handler which passes messages from the render window -// to the application class. -//----------------------------------------------------------------------- -DLLEXPORT LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - GraphicsClient *gc = (GraphicsClient*)GetWindowLongPtr (hWnd, GWLP_USERDATA); - if (gc) return gc->RenderWndProc (hWnd, uMsg, wParam, lParam); - else return DefWindowProc (hWnd, uMsg, wParam, lParam); -} - // ====================================================================== DLLEXPORT INT_PTR CALLBACK LaunchpadVideoWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) diff --git a/Src/Orbiter/Input.cpp b/Src/Orbiter/Input.cpp index 606452f5f..e85124e56 100644 --- a/Src/Orbiter/Input.cpp +++ b/Src/Orbiter/Input.cpp @@ -9,54 +9,20 @@ #include "Log.h" #include "Orbiter.h" -DInput::DInput (Orbiter *pOrbiter) +DInput::DInput (Orbiter *pOrbiter) : orbiter(pOrbiter), joystick(nullptr), joyprop(), m_hWnd(nullptr) { - orbiter = pOrbiter; - diframe = NULL; - m_hWnd = NULL; } DInput::~DInput () { - Destroy(); -} - -HRESULT DInput::Create (HINSTANCE hInst) -{ - if (NULL == (diframe = new CDIFramework7())) { - LOGOUT_ERR ("DirectInput: Could not create DI environment"); - return E_OUTOFMEMORY; - } - return diframe->Create (hInst); -} - -void DInput::Destroy () -{ - if (diframe) { - delete diframe; - diframe = NULL; - } + DestroyDevices(); } void DInput::SetRenderWindow(HWND hWnd) { - if (diframe) - diframe->DestroyDevices(); m_hWnd = hWnd; } -bool DInput::CreateKbdDevice() -{ - if (!m_hWnd) return false; // no render window defined - - if (FAILED (diframe->CreateKbdDevice (m_hWnd))) { - LOGOUT("ERROR: Could not create keyboard device"); - return false; // we need the keyboard, so give up - } - GetKbdDevice()->Acquire(); - return true; -} - bool DInput::CreateJoyDevice () { if (!m_hWnd) return false; // no render window defined @@ -64,34 +30,23 @@ bool DInput::CreateJoyDevice () Config *pcfg = orbiter->Cfg(); if (!pcfg->CfgJoystickPrm.Joy_idx) return false; // no joystick requested - if (FAILED (diframe->CreateJoyDevice (m_hWnd, pcfg->CfgJoystickPrm.Joy_idx-1))) { - LOGOUT_ERR("Could not create joystick device"); - return false; - } - - HRESULT hr = GetJoyDevice()->Acquire(); - if (hr == DIERR_OTHERAPPHASPRIO) { - Sleep(1000); - hr = GetJoyDevice()->Acquire(); - } - switch (hr) { - case DIERR_OTHERAPPHASPRIO: - hr = DI_OK; - break; - } - - if (SetJoystickProperties () != DI_OK) { - LOGOUT_ERR("Could not set joystick properties"); + joystick = SDL_OpenJoystick(pcfg->CfgJoystickPrm.Joy_idx); + if (!joystick) { + LOGOUT_ERR("Could not acquire joystick: %s", SDL_GetError()); return false; } + SetJoystickProperties(); return true; } void DInput::DestroyDevices () { - diframe->DestroyDevices(); + if (joystick) { + SDL_CloseJoystick(joystick); + joystick = nullptr; + } } void DInput::OptionChanged(DWORD cat, DWORD item) @@ -99,152 +54,78 @@ void DInput::OptionChanged(DWORD cat, DWORD item) if (cat == OPTCAT_JOYSTICK) { switch (item) { case OPTITEM_JOYSTICK_DEVICE: - diframe->DestroyJoyDevice(); + DestroyDevices(); CreateJoyDevice(); break; case OPTITEM_JOYSTICK_PARAM: SetJoystickProperties(); break; + default: break; } } } -bool DInput::PollJoystick (DIJOYSTATE2 *js) -{ - // todo: return joystick data in device-independent format - // allow collecting data from more than one joystick - - LPDIRECTINPUTDEVICE8 dev = GetJoyDevice(); - if (!dev) return false; - HRESULT hr = dev->Poll(); - //if (hr == DI_OK || hr == DI_NOEFFECT) // ignore error flag from poll. appears to occasionally return DIERR_UNPLUGGED - hr = dev->GetDeviceState (sizeof(DIJOYSTATE2), js); - if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) { - if (SUCCEEDED(dev->Acquire())) { - dev->Poll(); - hr = dev->GetDeviceState(sizeof(DIJOYSTATE2), js); - } - } - return (hr == S_OK); +bool DInput::PollJoystick (JoyState *js) const { + const auto& joyprm = orbiter->Cfg()->CfgJoystickPrm; + const auto deadzone = static_cast(joyprm.Deadzone); + js->xAx = PollAxisWithDeadzone(joyprop.xAxId, deadzone); + js->yAx = PollAxisWithDeadzone(joyprop.yAxId, deadzone); + if (joyprop.bRudder) { + js->zRot = PollAxisWithDeadzone(joyprop.zRotId, deadzone); + } else js->zRot = 0; + if (joyprop.bThrottle) { + js->throttle = PollAxisWithDeadzone(joyprop.ThrottleOfs, deadzone); + } else js->throttle = 0; + js->hat = SDL_GetJoystickHat(joystick, 0); + js->btn3 = SDL_GetJoystickButton(joystick, 2); + return true; } -HRESULT DInput::SetJoystickProperties () -{ - LPDIRECTINPUTDEVICE8 dev = GetJoyDevice(); - if (!dev) return DI_OK; - - HRESULT hr; - DIPROPRANGE diprg; - DIPROPDWORD diprw; - joyprop.bRudder = false; - joyprop.bThrottle = false; - Config *pcfg = orbiter->Cfg(); +Sint16 DInput::PollAxisWithDeadzone(const int axis, const Sint16 deadzone) const { + if (!joystick) return 0; - // x-axis range - diprg.diph.dwSize = sizeof (diprg); - diprg.diph.dwHeaderSize = sizeof (diprg.diph); - diprg.diph.dwObj = DIJOFS_X; - diprg.diph.dwHow = DIPH_BYOFFSET; - diprg.lMin = -1000; - diprg.lMax = +1000; - if ((hr = dev->SetProperty (DIPROP_RANGE, &diprg.diph)) != DI_OK) - return hr; - - // x-axis deadzone - diprw.diph.dwSize = sizeof (diprw); - diprw.diph.dwHeaderSize = sizeof (diprw.diph); - diprw.diph.dwObj = DIJOFS_X; - diprw.diph.dwHow = DIPH_BYOFFSET; - diprw.dwData = pcfg->CfgJoystickPrm.Deadzone; - if ((hr = dev->SetProperty (DIPROP_DEADZONE, &diprw.diph)) != DI_OK) - return hr; - - // y-axis range - diprg.diph.dwSize = sizeof (diprg); - diprg.diph.dwHeaderSize = sizeof (diprg.diph); - diprg.diph.dwObj = DIJOFS_Y; - diprg.diph.dwHow = DIPH_BYOFFSET; - diprg.lMin = -1000; - diprg.lMax = +1000; - if ((hr = dev->SetProperty (DIPROP_RANGE, &diprg.diph)) != DI_OK) - return hr; - - // y-axis deadzone - diprw.diph.dwSize = sizeof (diprw); - diprw.diph.dwHeaderSize = sizeof (diprw.diph); - diprw.diph.dwObj = DIJOFS_Y; - diprw.diph.dwHow = DIPH_BYOFFSET; - diprw.dwData = pcfg->CfgJoystickPrm.Deadzone; - if ((hr = dev->SetProperty (DIPROP_DEADZONE, &diprw.diph)) != DI_OK) - return hr; - - joyprop.bRudder = true; - joyprop.bThrottle = true; + const Sint16 axval = SDL_GetJoystickAxis(joystick, axis); + + if (abs(axval) < deadzone) return 0; + return axval; +} - diprg.diph.dwSize = sizeof (diprg); - diprg.diph.dwHeaderSize = sizeof (diprg.diph); - diprg.diph.dwObj = DIJOFS_RZ; - diprg.diph.dwHow = DIPH_BYOFFSET; - diprg.lMin = -1000; - diprg.lMax = +1000; - if (dev->SetProperty (DIPROP_RANGE, &diprg.diph) != DI_OK) - joyprop.bRudder = false; - - diprw.diph.dwSize = sizeof (diprw); - diprw.diph.dwHeaderSize = sizeof (diprw.diph); - diprw.diph.dwObj = DIJOFS_RZ; - diprw.diph.dwHow = DIPH_BYOFFSET; - diprw.dwData = pcfg->CfgJoystickPrm.Deadzone; - if (dev->SetProperty (DIPROP_DEADZONE, &diprw.diph) != DI_OK) - joyprop.bRudder = false; - - // z-axis range (throttle) - DWORD thaxis; - DIJOYSTATE2 js2; - switch (pcfg->CfgJoystickPrm.ThrottleAxis) { +void DInput::SetJoystickProperties() { + if (!joystick) return; + auto naxes = SDL_GetNumJoystickAxes(joystick); + joyprop.xAxId = 0; + joyprop.yAxId = 1; + if (naxes > 2) { + // Probably have a rudder axis. + // TODO: better input config + joyprop.bRudder = true; + joyprop.zRotId = 2; + } + joyprop.bThrottle = true; + switch (orbiter->Cfg()->CfgJoystickPrm.ThrottleAxis) { case 1: LOGOUT ("Joystick throttle: Z-AXIS"); - thaxis = DIJOFS_Z; - joyprop.ThrottleOfs = (BYTE*)&js2.lZ - (BYTE*)&js2; + joyprop.ThrottleOfs = 2; break; case 2: LOGOUT ("Joystick throttle: SLIDER 0"); - thaxis = DIJOFS_SLIDER(0); - joyprop.ThrottleOfs = (BYTE*)&js2.rglSlider[0] - (BYTE*)&js2; + joyprop.ThrottleOfs = 3; break; case 3: LOGOUT ("Joystick throttle: SLIDER 1"); - thaxis = DIJOFS_SLIDER(1); - joyprop.ThrottleOfs = (BYTE*)&js2.rglSlider[1] - (BYTE*)&js2; + joyprop.ThrottleOfs = 4; break; default: joyprop.bThrottle = false; LOGOUT ("Joystick throttle disabled by user"); - return DI_OK; + break; } +} - diprg.diph.dwSize = sizeof (diprg); - diprg.diph.dwHeaderSize = sizeof (diprg.diph); - diprg.diph.dwObj = thaxis; - diprg.diph.dwHow = DIPH_BYOFFSET; - diprg.lMin = -1000; - diprg.lMax = 0; - if ((hr = dev->SetProperty (DIPROP_RANGE, &diprg.diph)) != DI_OK) { - joyprop.bThrottle = false; - LOGOUT("No joystick throttle control detected"); - LOGOUT_DIERR(hr); - return DI_OK; - } - LOGOUT("Joystick throttle control detected"); - - // throttle saturation at extreme ends - diprw.diph.dwSize = sizeof (diprw); - diprw.diph.dwHeaderSize = sizeof (diprw.diph); - diprw.diph.dwObj = thaxis; - diprw.diph.dwHow = DIPH_BYOFFSET; - diprw.dwData = pcfg->CfgJoystickPrm.ThrottleSaturation; - if (dev->SetProperty (DIPROP_SATURATION, &diprw.diph) != DI_OK) { - LOGOUT_ERR("Setting joystick throttle saturation failed"); +bool DInput::ConsumeEvent(const SDL_Event &event) { + if (event.type == SDL_EVENT_KEY_UP || event.type == SDL_EVENT_KEY_DOWN) { + m_bufferedEvents.push_back(event.key); + return true; } - return DI_OK; + return false; } diff --git a/Src/Orbiter/Input.h b/Src/Orbiter/Input.h index 8a1c9ac52..4490d2a13 100644 --- a/Src/Orbiter/Input.h +++ b/Src/Orbiter/Input.h @@ -10,6 +10,18 @@ #include "Di7frame.h" +#include +#include + +struct JoyState { + Sint16 xAx; + Sint16 yAx; + Sint16 zRot; + Sint16 throttle; + Uint8 hat; + bool btn3; +}; + class DInput { friend class Orbiter; @@ -17,37 +29,39 @@ class DInput { DInput (Orbiter *pOrbiter); ~DInput (); - HRESULT Create (HINSTANCE hInst); - void Destroy (); - void SetRenderWindow(HWND hWnd); - bool CreateKbdDevice(); bool CreateJoyDevice (); void DestroyDevices (); - inline CDIFramework7 *GetDIFrame() const { return diframe; } - inline const LPDIRECTINPUTDEVICE8 GetKbdDevice() const { return diframe->GetKbdDevice(); } - inline const LPDIRECTINPUTDEVICE8 GetJoyDevice() const { return diframe->GetJoyDevice(); } + inline SDL_Joystick* GetJoyDevice() const { return joystick; } void OptionChanged(DWORD cat, DWORD item); - bool PollJoystick (DIJOYSTATE2 *js); + bool PollJoystick (JoyState *js) const; struct JoyProp { bool bThrottle; // joystick has throttle control bool bRudder; // joystick has rudder control - int ThrottleOfs; // throttle data offset + int ThrottleOfs; // throttle axis ID + int xAxId; // X-axis ID + int yAxId; // Y-axis ID + int zRotId; // Z rotation axis ID }; + bool ConsumeEvent(const SDL_Event &event); + protected: - HRESULT SetJoystickProperties (); + void SetJoystickProperties (); private: Orbiter *orbiter; - CDIFramework7 *diframe; + SDL_Joystick* joystick; JoyProp joyprop; HWND m_hWnd; + std::list m_bufferedEvents; + + Sint16 PollAxisWithDeadzone(int axis, Sint16 deadzone) const; }; #endif // !__INPUT_H \ No newline at end of file diff --git a/Src/Orbiter/Keymap.cpp b/Src/Orbiter/Keymap.cpp index 86c216c5c..b39254ffe 100644 --- a/Src/Orbiter/Keymap.cpp +++ b/Src/Orbiter/Keymap.cpp @@ -114,41 +114,41 @@ struct { WORD defkey; const char *itemstr; } lkeyspec[LKEY_COUNT] = { - {OAPI_KEY_LEFT | KMOD_ALT, "CockpitCamRotateLeft"}, - {OAPI_KEY_RIGHT | KMOD_ALT, "CockpitCamRotateRight"}, - {OAPI_KEY_UP | KMOD_ALT, "CockpitCamRotateUp"}, - {OAPI_KEY_DOWN | KMOD_ALT, "CockpitCamRotateDown"}, - {OAPI_KEY_DOWN | KMOD_CTRL | KMOD_ALT, "CockpitCamDontLean"}, - {OAPI_KEY_UP | KMOD_CTRL | KMOD_ALT, "CockpitCamLeanForward"}, - {OAPI_KEY_LEFT | KMOD_CTRL | KMOD_ALT, "CockpitCamLeanLeft"}, - {OAPI_KEY_RIGHT | KMOD_CTRL | KMOD_ALT, "CockpitCamLeanRight"}, + {OAPI_KEY_LEFT | OKMOD_ALT, "CockpitCamRotateLeft"}, + {OAPI_KEY_RIGHT | OKMOD_ALT, "CockpitCamRotateRight"}, + {OAPI_KEY_UP | OKMOD_ALT, "CockpitCamRotateUp"}, + {OAPI_KEY_DOWN | OKMOD_ALT, "CockpitCamRotateDown"}, + {OAPI_KEY_DOWN | OKMOD_CTRL | OKMOD_ALT, "CockpitCamDontLean"}, + {OAPI_KEY_UP | OKMOD_CTRL | OKMOD_ALT, "CockpitCamLeanForward"}, + {OAPI_KEY_LEFT | OKMOD_CTRL | OKMOD_ALT, "CockpitCamLeanLeft"}, + {OAPI_KEY_RIGHT | OKMOD_CTRL | OKMOD_ALT, "CockpitCamLeanRight"}, {OAPI_KEY_HOME, "CockpitResetCam"}, {OAPI_KEY_LEFT, "PanelShiftLeft"}, {OAPI_KEY_RIGHT, "PanelShiftRight"}, {OAPI_KEY_UP, "PanelShiftUp"}, {OAPI_KEY_DOWN, "PanelShiftDown"}, - {OAPI_KEY_LEFT | KMOD_CTRL, "PanelSwitchLeft"}, - {OAPI_KEY_RIGHT | KMOD_CTRL, "PanelSwitchRight"}, - {OAPI_KEY_UP | KMOD_CTRL, "PanelSwitchUp"}, - {OAPI_KEY_DOWN | KMOD_CTRL, "PanelSwitchDown"}, - {OAPI_KEY_LEFT | KMOD_CTRL, "TrackCamRotateLeft"}, - {OAPI_KEY_RIGHT | KMOD_CTRL, "TrackCamRotateRight"}, - {OAPI_KEY_UP | KMOD_CTRL, "TrackCamRotateUp"}, - {OAPI_KEY_DOWN | KMOD_CTRL, "TrackCamRotateDown"}, + {OAPI_KEY_LEFT | OKMOD_CTRL, "PanelSwitchLeft"}, + {OAPI_KEY_RIGHT | OKMOD_CTRL, "PanelSwitchRight"}, + {OAPI_KEY_UP | OKMOD_CTRL, "PanelSwitchUp"}, + {OAPI_KEY_DOWN | OKMOD_CTRL, "PanelSwitchDown"}, + {OAPI_KEY_LEFT | OKMOD_CTRL, "TrackCamRotateLeft"}, + {OAPI_KEY_RIGHT | OKMOD_CTRL, "TrackCamRotateRight"}, + {OAPI_KEY_UP | OKMOD_CTRL, "TrackCamRotateUp"}, + {OAPI_KEY_DOWN | OKMOD_CTRL, "TrackCamRotateDown"}, {OAPI_KEY_NEXT, "TrackCamAdvance"}, {OAPI_KEY_PRIOR, "TrackCamRetreat"}, {OAPI_KEY_LEFT, "GroundCamTiltLeft"}, {OAPI_KEY_RIGHT, "GroundCamTiltRight"}, {OAPI_KEY_UP, "GroundCamTiltUp"}, {OAPI_KEY_DOWN, "GroundCamTiltDown"}, - {OAPI_KEY_ADD | KMOD_CTRL, "IncMainThrust"}, - {OAPI_KEY_SUBTRACT | KMOD_CTRL, "DecMainThrust"}, + {OAPI_KEY_ADD | OKMOD_CTRL, "IncMainThrust"}, + {OAPI_KEY_SUBTRACT | OKMOD_CTRL, "DecMainThrust"}, {OAPI_KEY_MULTIPLY, "KillMainRetroThrust"}, {OAPI_KEY_ADD, "OverrideFullMainThrust"}, {OAPI_KEY_SUBTRACT, "OverrideFullRetroThrust"}, {OAPI_KEY_NUMPAD0, "IncHoverThrust"}, {OAPI_KEY_DECIMAL, "DecHoverThrust"}, - {OAPI_KEY_DIVIDE | KMOD_CTRL, "RCSEnable"}, + {OAPI_KEY_DIVIDE | OKMOD_CTRL, "RCSEnable"}, {OAPI_KEY_DIVIDE, "RCSMode"}, {OAPI_KEY_NUMPAD2, "RCSPitchUp"}, {OAPI_KEY_NUMPAD8, "RCSPitchDown"}, @@ -162,18 +162,18 @@ struct { {OAPI_KEY_NUMPAD3, "RCSRight"}, {OAPI_KEY_NUMPAD6, "RCSForward"}, {OAPI_KEY_NUMPAD9, "RCSBack"}, - {OAPI_KEY_NUMPAD2 | KMOD_CTRL, "LPRCSPitchUp"}, - {OAPI_KEY_NUMPAD8 | KMOD_CTRL, "LPRCSPitchDown"}, - {OAPI_KEY_NUMPAD1 | KMOD_CTRL, "LPRCSYawLeft"}, - {OAPI_KEY_NUMPAD3 | KMOD_CTRL, "LPRCSYawRight"}, - {OAPI_KEY_NUMPAD4 | KMOD_CTRL, "LPRCSBankLeft"}, - {OAPI_KEY_NUMPAD6 | KMOD_CTRL, "LPRCSBankRight"}, - {OAPI_KEY_NUMPAD2 | KMOD_CTRL, "LPRCSUp"}, - {OAPI_KEY_NUMPAD8 | KMOD_CTRL, "LPRCSDown"}, - {OAPI_KEY_NUMPAD1 | KMOD_CTRL, "LPRCSLeft"}, - {OAPI_KEY_NUMPAD3 | KMOD_CTRL, "LPRCSRight"}, - {OAPI_KEY_NUMPAD6 | KMOD_CTRL, "LPRCSForward"}, - {OAPI_KEY_NUMPAD9 | KMOD_CTRL, "LPRCSBack"}, + {OAPI_KEY_NUMPAD2 | OKMOD_CTRL, "LPRCSPitchUp"}, + {OAPI_KEY_NUMPAD8 | OKMOD_CTRL, "LPRCSPitchDown"}, + {OAPI_KEY_NUMPAD1 | OKMOD_CTRL, "LPRCSYawLeft"}, + {OAPI_KEY_NUMPAD3 | OKMOD_CTRL, "LPRCSYawRight"}, + {OAPI_KEY_NUMPAD4 | OKMOD_CTRL, "LPRCSBankLeft"}, + {OAPI_KEY_NUMPAD6 | OKMOD_CTRL, "LPRCSBankRight"}, + {OAPI_KEY_NUMPAD2 | OKMOD_CTRL, "LPRCSUp"}, + {OAPI_KEY_NUMPAD8 | OKMOD_CTRL, "LPRCSDown"}, + {OAPI_KEY_NUMPAD1 | OKMOD_CTRL, "LPRCSLeft"}, + {OAPI_KEY_NUMPAD3 | OKMOD_CTRL, "LPRCSRight"}, + {OAPI_KEY_NUMPAD6 | OKMOD_CTRL, "LPRCSForward"}, + {OAPI_KEY_NUMPAD9 | OKMOD_CTRL, "LPRCSBack"}, {OAPI_KEY_A, "NMHoldAltitude"}, {OAPI_KEY_L, "NMHLevel"}, {OAPI_KEY_LBRACKET, "NMPrograde"}, @@ -181,43 +181,43 @@ struct { {OAPI_KEY_SEMICOLON, "NMNormal"}, {OAPI_KEY_APOSTROPHE, "NMAntinormal"}, {OAPI_KEY_NUMPAD5, "NMKillrot"}, - {OAPI_KEY_D | KMOD_CTRL, "Undock"}, + {OAPI_KEY_D | OKMOD_CTRL, "Undock"}, {OAPI_KEY_DELETE, "IncElevatorTrim"}, {OAPI_KEY_INSERT, "DecElevatorTrim"}, {OAPI_KEY_COMMA, "WheelbrakeLeft"}, {OAPI_KEY_PERIOD, "WheelbrakeRight"}, - {OAPI_KEY_H | KMOD_CTRL, "HUD"}, + {OAPI_KEY_H | OKMOD_CTRL, "HUD"}, {OAPI_KEY_H, "HUDMode"}, - {OAPI_KEY_R | KMOD_CTRL, "HUDReference"}, - {OAPI_KEY_R | KMOD_CTRL | KMOD_ALT, "HUDTarget"}, - {OAPI_KEY_H | KMOD_ALT, "HUDColour"}, + {OAPI_KEY_R | OKMOD_CTRL, "HUDReference"}, + {OAPI_KEY_R | OKMOD_CTRL | OKMOD_ALT, "HUDTarget"}, + {OAPI_KEY_H | OKMOD_ALT, "HUDColour"}, {OAPI_KEY_T, "IncSimSpeed"}, {OAPI_KEY_R, "DecSimSpeed"}, {OAPI_KEY_X, "IncFOV"}, {OAPI_KEY_Z, "DecFOV"}, - {OAPI_KEY_X | KMOD_CTRL, "StepIncFOV"}, - {OAPI_KEY_Z | KMOD_CTRL, "StepDecFOV"}, + {OAPI_KEY_X | OKMOD_CTRL, "StepIncFOV"}, + {OAPI_KEY_Z | OKMOD_CTRL, "StepDecFOV"}, {OAPI_KEY_F4, "MainMenu"}, - {OAPI_KEY_F1 | KMOD_ALT, "DlgHelp"}, - {OAPI_KEY_F1 | KMOD_CTRL, "DlgCamera"}, - {OAPI_KEY_F2 | KMOD_CTRL, "DlgSimspeed"}, - {OAPI_KEY_F4 | KMOD_CTRL, "DlgCustomCmd"}, - {OAPI_KEY_F9 | KMOD_CTRL, "DlgVisualHelpers"}, - {OAPI_KEY_F5 | KMOD_CTRL, "DlgRecorder"}, - {OAPI_KEY_I | KMOD_CTRL, "DlgInfo"}, - {OAPI_KEY_M | KMOD_CTRL, "DlgMap"}, + {OAPI_KEY_F1 | OKMOD_ALT, "DlgHelp"}, + {OAPI_KEY_F1 | OKMOD_CTRL, "DlgCamera"}, + {OAPI_KEY_F2 | OKMOD_CTRL, "DlgSimspeed"}, + {OAPI_KEY_F4 | OKMOD_CTRL, "DlgCustomCmd"}, + {OAPI_KEY_F9 | OKMOD_CTRL, "DlgVisualHelpers"}, + {OAPI_KEY_F5 | OKMOD_CTRL, "DlgRecorder"}, + {OAPI_KEY_I | OKMOD_CTRL, "DlgInfo"}, + {OAPI_KEY_M | OKMOD_CTRL, "DlgMap"}, {OAPI_KEY_F1, "ToggleCamInternal"}, {OAPI_KEY_F2, "ToggleTrackMode"}, {OAPI_KEY_F8, "TogglePanelMode"}, {OAPI_KEY_F9, "TogglePlanetarium"}, - {OAPI_KEY_F9 | KMOD_ALT, "ToggleSurfaceLabels"}, - {OAPI_KEY_C | KMOD_CTRL, "ToggleRecPlay"}, - {OAPI_KEY_P | KMOD_CTRL, "Pause"}, - {OAPI_KEY_S | KMOD_CTRL, "Quicksave"}, - {OAPI_KEY_Q | KMOD_CTRL, "Quit"}, + {OAPI_KEY_F9 | OKMOD_ALT, "ToggleSurfaceLabels"}, + {OAPI_KEY_C | OKMOD_CTRL, "ToggleRecPlay"}, + {OAPI_KEY_P | OKMOD_CTRL, "Pause"}, + {OAPI_KEY_S | OKMOD_CTRL, "Quicksave"}, + {OAPI_KEY_Q | OKMOD_CTRL, "Quit"}, {OAPI_KEY_F3, "DlgSelectVessel"}, - {OAPI_KEY_F3 | KMOD_CTRL, "SelectPrevVessel"}, - {OAPI_KEY_SYSRQ | KMOD_CTRL, "DlgCapture"}, + {OAPI_KEY_F3 | OKMOD_CTRL, "SelectPrevVessel"}, + {OAPI_KEY_SYSRQ | OKMOD_CTRL, "DlgCapture"}, {OAPI_KEY_F6, "DlgOptions"} }; @@ -278,10 +278,10 @@ bool Keymap::IsLogicalKey (DWORD &key, char *kstate, int lfunc, bool clearkey) c bool Keymap::IsMatchingModifier (char *kstate, WORD key) const { WORD kmod; - if (kmod = (key & KMOD_CTRL)) { - if (kmod == KMOD_CTRL) { + if (kmod = (key & OKMOD_CTRL)) { + if (kmod == OKMOD_CTRL) { if (!KEYMOD_CONTROL(kstate)) return false; - } else if (kmod == KMOD_LCTRL) { + } else if (kmod == OKMOD_LCTRL) { if (!KEYMOD_LCONTROL(kstate)) return false; } else { if (!KEYMOD_RCONTROL(kstate)) return false; @@ -290,10 +290,10 @@ bool Keymap::IsMatchingModifier (char *kstate, WORD key) const if (KEYMOD_CONTROL(kstate)) return false; } - if (kmod = (key & KMOD_SHIFT)) { - if (kmod == KMOD_SHIFT) { + if (kmod = (key & OKMOD_SHIFT)) { + if (kmod == OKMOD_SHIFT) { if (!KEYMOD_SHIFT(kstate)) return false; - } else if (kmod == KMOD_LSHIFT) { + } else if (kmod == OKMOD_LSHIFT) { if (!KEYMOD_LSHIFT(kstate)) return false; } else { if (!KEYMOD_RSHIFT(kstate)) return false; @@ -302,10 +302,10 @@ bool Keymap::IsMatchingModifier (char *kstate, WORD key) const if (KEYMOD_SHIFT(kstate)) return false; } - if (kmod = (key & KMOD_ALT)) { - if (kmod == KMOD_ALT) { + if (kmod = (key & OKMOD_ALT)) { + if (kmod == OKMOD_ALT) { if (!KEYMOD_ALT(kstate)) return false; - } else if (kmod == KMOD_LALT) { + } else if (kmod == OKMOD_LALT) { if (!KEYMOD_LALT(kstate)) return false; } else { if (!KEYMOD_RALT(kstate)) return false; @@ -329,15 +329,15 @@ bool Keymap::ScanStr (char *cbuf, WORD &key) const for (;;) { tok = strtok (NULL, " "); if (!tok) break; - if (!_stricmp (tok, "LSHIFT")) key |= KMOD_LSHIFT; - else if (!_stricmp (tok, "RSHIFT")) key |= KMOD_RSHIFT; - else if (!_stricmp (tok, "SHIFT")) key |= KMOD_SHIFT; - else if (!_stricmp (tok, "LCTRL")) key |= KMOD_LCTRL; - else if (!_stricmp (tok, "RCTRL")) key |= KMOD_RCTRL; - else if (!_stricmp (tok, "CTRL")) key |= KMOD_CTRL; - else if (!_stricmp (tok, "LALT")) key |= KMOD_LALT; - else if (!_stricmp (tok, "RALT")) key |= KMOD_RALT; - else if (!_stricmp (tok, "ALT")) key |= KMOD_ALT; + if (!_stricmp (tok, "LSHIFT")) key |= OKMOD_LSHIFT; + else if (!_stricmp (tok, "RSHIFT")) key |= OKMOD_RSHIFT; + else if (!_stricmp (tok, "SHIFT")) key |= OKMOD_SHIFT; + else if (!_stricmp (tok, "LCTRL")) key |= OKMOD_LCTRL; + else if (!_stricmp (tok, "RCTRL")) key |= OKMOD_RCTRL; + else if (!_stricmp (tok, "CTRL")) key |= OKMOD_CTRL; + else if (!_stricmp (tok, "LALT")) key |= OKMOD_LALT; + else if (!_stricmp (tok, "RALT")) key |= OKMOD_RALT; + else if (!_stricmp (tok, "ALT")) key |= OKMOD_ALT; } return true; } @@ -352,25 +352,25 @@ char *Keymap::PrintStr (char *cbuf, WORD &key) const cbuf[0] = '\0'; } else { strcpy (cbuf, keyname[i].name); - if (kmod = (key & KMOD_SHIFT)) { + if (kmod = (key & OKMOD_SHIFT)) { switch (kmod) { - case KMOD_SHIFT: strcat (cbuf, " SHIFT"); break; - case KMOD_LSHIFT: strcat (cbuf, " LSHIFT"); break; - case KMOD_RSHIFT: strcat (cbuf, " RSHIFT"); break; + case OKMOD_SHIFT: strcat (cbuf, " SHIFT"); break; + case OKMOD_LSHIFT: strcat (cbuf, " LSHIFT"); break; + case OKMOD_RSHIFT: strcat (cbuf, " RSHIFT"); break; } } - if (kmod = (key & KMOD_CTRL)) { + if (kmod = (key & OKMOD_CTRL)) { switch (kmod) { - case KMOD_CTRL: strcat (cbuf, " CTRL"); break; - case KMOD_LCTRL: strcat (cbuf, " LCTRL"); break; - case KMOD_RCTRL: strcat (cbuf, " RCTRL"); break; + case OKMOD_CTRL: strcat (cbuf, " CTRL"); break; + case OKMOD_LCTRL: strcat (cbuf, " LCTRL"); break; + case OKMOD_RCTRL: strcat (cbuf, " RCTRL"); break; } } - if (kmod = (key & KMOD_ALT)) { + if (kmod = (key & OKMOD_ALT)) { switch (kmod) { - case KMOD_ALT: strcat (cbuf, " ALT"); break; - case KMOD_LALT: strcat (cbuf, " LALT"); break; - case KMOD_RALT: strcat (cbuf, " RALT"); break; + case OKMOD_ALT: strcat (cbuf, " ALT"); break; + case OKMOD_LALT: strcat (cbuf, " LALT"); break; + case OKMOD_RALT: strcat (cbuf, " RALT"); break; } } } diff --git a/Src/Orbiter/Keymap.h b/Src/Orbiter/Keymap.h index fd1daecd8..c5e0f7b6b 100644 --- a/Src/Orbiter/Keymap.h +++ b/Src/Orbiter/Keymap.h @@ -12,15 +12,15 @@ #include "Orbitersdk.h" // key modifier list -#define KMOD_LSHIFT 0x0100 -#define KMOD_RSHIFT 0x0200 -#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) -#define KMOD_LCTRL 0x0400 -#define KMOD_RCTRL 0x0800 -#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) -#define KMOD_LALT 0x1000 -#define KMOD_RALT 0x2000 -#define KMOD_ALT (KMOD_LALT|KMOD_RALT) +#define OKMOD_LSHIFT 0x0100 +#define OKMOD_RSHIFT 0x0200 +#define OKMOD_SHIFT (OKMOD_LSHIFT|OKMOD_RSHIFT) +#define OKMOD_LCTRL 0x0400 +#define OKMOD_RCTRL 0x0800 +#define OKMOD_CTRL (OKMOD_LCTRL|OKMOD_RCTRL) +#define OKMOD_LALT 0x1000 +#define OKMOD_RALT 0x2000 +#define OKMOD_ALT (OKMOD_LALT|OKMOD_RALT) class Keymap { public: diff --git a/Src/Orbiter/Launchpad.cpp b/Src/Orbiter/Launchpad.cpp index b5bbbcd93..760a42aa9 100644 --- a/Src/Orbiter/Launchpad.cpp +++ b/Src/Orbiter/Launchpad.cpp @@ -324,6 +324,7 @@ INT_PTR orbiter::LaunchpadDialog::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, if (pCfg->CfgDemoPrm.bBlockExit) return TRUE; UpdateConfig (); DestroyWindow (hWnd); + g_pOrbiter->SetShouldQuit(); return TRUE; case WM_DESTROY: if (pCfg->CfgDemoPrm.bDemo && timerid) { @@ -430,6 +431,7 @@ INT_PTR orbiter::LaunchpadDialog::DlgProc (HWND hWnd, UINT uMsg, WPARAM wParam, PostMessage (hWnd, WM_COMMAND, IDLAUNCH, 0); } return 0; + default: break; } return FALSE; } diff --git a/Src/Orbiter/MenuInfoBar.cpp b/Src/Orbiter/MenuInfoBar.cpp index 36acf51af..9a2b45974 100644 --- a/Src/Orbiter/MenuInfoBar.cpp +++ b/Src/Orbiter/MenuInfoBar.cpp @@ -624,11 +624,11 @@ void MenuInfoBar::Update (double t) } } -bool MenuInfoBar::ProcessMouse (UINT event, DWORD state, DWORD x, DWORD y) +bool MenuInfoBar::ProcessMouse (const SDL_Event &event, DWORD x, DWORD y) { x = (DWORD)(x / transf.m11); // account for menu squeezing - if (event == WM_MOUSEMOVE) { + if (event.type == SDL_EVENT_MOUSE_MOTION) { if (menumode == 2) { if (y <= scrollzone && menustate != 1) { menustate = 2; @@ -658,7 +658,7 @@ bool MenuInfoBar::ProcessMouse (UINT event, DWORD state, DWORD x, DWORD y) itemHighlight = item; } } - if (event == WM_LBUTTONDOWN && itemHighlight >= 0) { + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT && itemHighlight >= 0) { switch (itemHighlight) { case 0: g_pOrbiter->DlgMgr()->EnsureEntry (); @@ -697,11 +697,11 @@ bool MenuInfoBar::ProcessMouse (UINT event, DWORD state, DWORD x, DWORD y) return true; case 11: g_pOrbiter->PreCloseSession(); - DestroyWindow (g_pOrbiter->GetRenderWnd()); + g_pOrbiter->CloseSession(); return true; } } - if (event == WM_RBUTTONDOWN && y <= scrollpos && x >= menuX && x < menuX+menuW) { + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT && y <= scrollpos && x >= menuX && x < menuX+menuW) { g_pOrbiter->DlgMgr()->EnsureEntry (); return true; } diff --git a/Src/Orbiter/MenuInfoBar.h b/Src/Orbiter/MenuInfoBar.h index 127527c5c..4cc288146 100644 --- a/Src/Orbiter/MenuInfoBar.h +++ b/Src/Orbiter/MenuInfoBar.h @@ -27,7 +27,7 @@ class MenuInfoBar { MenuInfoBar (const Pane *_pane); ~MenuInfoBar (); void Update (double t); - bool ProcessMouse (UINT event, DWORD state, DWORD x, DWORD y); + bool ProcessMouse (const SDL_Event &event, DWORD x, DWORD y); void Render (); void SetFOV (double fov); void SetWarp (double warp); diff --git a/Src/Orbiter/Mfd.h b/Src/Orbiter/Mfd.h index 8fdc646b0..78bc9501a 100644 --- a/Src/Orbiter/Mfd.h +++ b/Src/Orbiter/Mfd.h @@ -21,6 +21,7 @@ #include "Vessel.h" #include "Element.h" #include "Select.h" +#include #define ELN 256 // polygon resolution for orbit trajectory #define ELNH (ELN/2) @@ -42,8 +43,6 @@ struct MFDMODE { // MFD mode (for builtin and user-defined modes) }; class Pane; -class InputBox; -class Select; class oapi::GraphicsClient; static char work_kstate[256]; diff --git a/Src/Orbiter/MfdMap.cpp b/Src/Orbiter/MfdMap.cpp index cacba0d80..73d20c098 100644 --- a/Src/Orbiter/MfdMap.cpp +++ b/Src/Orbiter/MfdMap.cpp @@ -518,15 +518,8 @@ void Instrument_Map::UpdateBlt () if (disp_mode) return; if (!gc) return; map->Update (); -#ifdef ASYNC_DRAWMAP - if (!map->ThreadBusy()) { - gc->clbkCopyBitmap (surf, map->GetMap(), 0, 0, IW, IH); - map->AsyncDrawMap (); - } -#else map->DrawMap (); - gc->clbkCopyBitmap (surf, map->GetMap(), 0, 0, IW, IH); -#endif + oapiBlt (surf, map->GetMap(), 0, 0, 0, 0, IW, IH); } // ======================================================================= @@ -654,8 +647,8 @@ bool Instrument_Map::ToggleDispParam (int which) int i; for (i = 0; i < cms.nset; i++) if (cms.set[i].active) break; - if (i < cms.nset) dispflag = dispflag | DISP_CUSTOM1; - else dispflag = dispflag & ~DISP_CUSTOM1; + if (i < cms.nset) dispflag = dispflag | DISP_CUSTOMMARKER; + else dispflag = dispflag & ~DISP_CUSTOMMARKER; } return true; } diff --git a/Src/Orbiter/MfdMap_old.cpp b/Src/Orbiter/MfdMap_old.cpp index 9a2df67d4..716689315 100644 --- a/Src/Orbiter/MfdMap_old.cpp +++ b/Src/Orbiter/MfdMap_old.cpp @@ -341,15 +341,15 @@ void Instrument_MapOld::UpdateDraw (oapi::Sketchpad *skp) strcpy (datastr[0]+13, btgt->Name()); skp->Text (x1, y+dy, datastr[0], strlen(datastr[0])); btgt->EquPos (blng, blat); - sprintf (cbuf, " Pos: %6.2fº%c %6.2fº%c", + sprintf (cbuf, " Pos: %6.2f�%c %6.2f�%c", Deg(fabs(blng)), blng >= 0.0 ? 'E':'W', Deg(fabs(blat)), blat >= 0.0 ? 'N':'S'); skp->Text (x1, y+2*dy, cbuf, strlen (cbuf)); if (sp) { rad = refplanet->Size(); Orthodome (sp->lng, sp->lat, blng, blat, adist, hdg); - sprintf (cbuf, " Dst: %s (%0.2fº)", DistStr (adist*rad), Deg(adist)); + sprintf (cbuf, " Dst: %s (%0.2f�)", DistStr (adist*rad), Deg(adist)); skp->Text (x1, y+3*dy, cbuf, strlen (cbuf)); - sprintf (cbuf, " Dir: %6.2fº", Deg(hdg)); + sprintf (cbuf, " Dir: %6.2f�", Deg(hdg)); skp->Text (x1, y+4*dy, cbuf, strlen (cbuf)); } } else { @@ -366,7 +366,7 @@ void Instrument_MapOld::UpdateDraw (oapi::Sketchpad *skp) if (tel && otgt->ElRef() == refplanet) { double lng, lat, r; refplanet->GlobalToEquatorial (otgt->GPos(), lng, lat, r); - sprintf (cbuf, " Pos: %6.2fº%c %6.2fº%c", + sprintf (cbuf, " Pos: %6.2f�%c %6.2f�%c", Deg(fabs(lng)), lng >= 0.0 ? 'E':'W', Deg(fabs(lat)), lat >= 0.0 ? 'N':'S'); skp->Text (x1, y, cbuf, strlen (cbuf)); y += dy; sprintf (cbuf, " Alt: %s", DistStr (r - refplanet->Size())); @@ -557,7 +557,7 @@ void Instrument_MapOld::CalcOrbitProj (const Elements *el, const Planet *planet, for (i = 0; i < npt05; i++) { rl.Set (mul (R, Vector(cosp[i],0,sinp[i]))); x = rl.x, y = rl.y, z = rl.z; - lng = atan2 (z,x) + Pi; // maps start at -Pi (180°W) + lng = atan2 (z,x) + Pi; // maps start at -Pi (180�W) lat = atan(y/std::hypot(x,z)); sp[i].x = (int)(lng*f1); if ((sp[i+npt05].x = sp[i].x + mapw05) >= mapw) sp[i+npt05].x -= mapw; diff --git a/Src/Orbiter/ModuleAPI.cpp b/Src/Orbiter/ModuleAPI.cpp index c491f083c..10363b059 100644 --- a/Src/Orbiter/ModuleAPI.cpp +++ b/Src/Orbiter/ModuleAPI.cpp @@ -63,7 +63,7 @@ void Module::clbkSimulationStart (RenderMode mode) { // backward compatibility call (deprecated) void (*opcOpenRenderViewport)(HWND,DWORD,DWORD,BOOL) = (void(*)(HWND,DWORD,DWORD,BOOL))GetProcAddress (hModule, "opcOpenRenderViewport"); - if (opcOpenRenderViewport) opcOpenRenderViewport (g_pOrbiter->GetRenderWnd(), g_pOrbiter->ViewW(), g_pOrbiter->ViewH(), g_pOrbiter->IsFullscreen()?TRUE:FALSE); + if (opcOpenRenderViewport) opcOpenRenderViewport (g_pOrbiter->GetRenderWnd()->Win32Handle(), g_pOrbiter->ViewW(), g_pOrbiter->ViewH(), g_pOrbiter->IsFullscreen()?TRUE:FALSE); } // ====================================================================== diff --git a/Src/Orbiter/OptionsPages.cpp b/Src/Orbiter/OptionsPages.cpp index bb22b42e1..19f728785 100644 --- a/Src/Orbiter/OptionsPages.cpp +++ b/Src/Orbiter/OptionsPages.cpp @@ -1147,10 +1147,19 @@ BOOL OptionsPage_UI::OnCommand(HWND hPage, WORD ctrlId, WORD notification, HWND // ====================================================================== OptionsPage_Joystick::OptionsPage_Joystick(OptionsPageContainer* container) - : OptionsPage(container) + : OptionsPage(container), m_joylist(nullptr), m_njoy(0) { } + OptionsPage_Joystick::~OptionsPage_Joystick() { + if (m_joylist) { + SDL_free(m_joylist); + m_joylist = nullptr; + m_njoy = 0; + } +} + + // ---------------------------------------------------------------------- int OptionsPage_Joystick::ResourceId() const @@ -1180,16 +1189,24 @@ void OptionsPage_Joystick::UpdateControls(HWND hPage) { char cbuf[256]; - SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_SETCURSEL, (WPARAM)Cfg()->CfgJoystickPrm.Joy_idx, 0); + auto joyix = 0; + for (int i = 0; i < m_njoy; ++i) { + if (Cfg()->CfgJoystickPrm.Joy_idx == m_joylist[i]) { + joyix = i + 1; + break; + } + } + + SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_SETCURSEL, (WPARAM)joyix, 0); SendDlgItemMessage(hPage, IDC_OPT_JOY_THROTTLE, CB_SETCURSEL, (WPARAM)Cfg()->CfgJoystickPrm.ThrottleAxis, 0); SendDlgItemMessage(hPage, IDC_OPT_JOY_INIT, BM_SETCHECK, Cfg()->CfgJoystickPrm.bThrottleIgnore ? BST_CHECKED : BST_UNCHECKED, 0); - int sat = Cfg()->CfgJoystickPrm.ThrottleSaturation / 10; + int sat = Cfg()->CfgJoystickPrm.ThrottleSaturation; oapiSetGaugePos(GetDlgItem(hPage, IDC_OPT_JOY_SAT), sat); sprintf(cbuf, "%d", sat); SetWindowText(GetDlgItem(hPage, IDC_OPT_JOY_STATIC1), cbuf); - int dz = Cfg()->CfgJoystickPrm.Deadzone / 10; + int dz = Cfg()->CfgJoystickPrm.Deadzone; oapiSetGaugePos(GetDlgItem(hPage, IDC_OPT_JOY_DEAD), dz); sprintf(cbuf, "%d", dz); SetWindowText(GetDlgItem(hPage, IDC_OPT_JOY_STATIC2), cbuf); @@ -1210,21 +1227,19 @@ BOOL OptionsPage_Joystick::OnInitDialog(HWND hPage, WPARAM wParam, LPARAM lParam { OptionsPage::OnInitDialog(hPage, wParam, lParam); - DWORD ndev; - DIDEVICEINSTANCE* joylist; - g_pOrbiter->GetDInput()->GetJoysticks(&joylist, &ndev); + m_joylist = SDL_GetJoysticks(&m_njoy); SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_ADDSTRING, 0, (LPARAM)""); - for (int i = 0; i < ndev; i++) - SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_ADDSTRING, 0, (LPARAM)(joylist[i].tszProductName)); + for (int i = 0; i < m_njoy; i++) + SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_ADDSTRING, 0, (LPARAM)(SDL_GetJoystickNameForID(m_joylist[i]))); const char* thmode[4] = { "", "Z-axis", "Slider 0", "Slider 1" }; SendDlgItemMessage(hPage, IDC_OPT_JOY_THROTTLE, CB_RESETCONTENT, 0, 0); for (int i = 0; i < ARRAYSIZE(thmode); i++) SendDlgItemMessage(hPage, IDC_OPT_JOY_THROTTLE, CB_ADDSTRING, 0, (LPARAM)thmode[i]); - GAUGEPARAM gp = { 0, 1000, GAUGEPARAM::LEFT, GAUGEPARAM::BLACK }; + GAUGEPARAM gp = { 0, 32768, GAUGEPARAM::LEFT, GAUGEPARAM::BLACK }; oapiSetGaugeParams(GetDlgItem(hPage, IDC_OPT_JOY_SAT), &gp); oapiSetGaugeParams(GetDlgItem(hPage, IDC_OPT_JOY_DEAD), &gp); @@ -1239,7 +1254,7 @@ BOOL OptionsPage_Joystick::OnCommand(HWND hPage, WORD ctrlId, WORD notification, case IDC_OPT_JOY_DEVICE: if (notification == CBN_SELCHANGE) { DWORD idx = (DWORD)SendDlgItemMessage(hPage, IDC_OPT_JOY_DEVICE, CB_GETCURSEL, 0, 0); - Cfg()->CfgJoystickPrm.Joy_idx = idx; + Cfg()->CfgJoystickPrm.Joy_idx = m_joylist[idx - 1]; g_pOrbiter->OnOptionChanged(OPTCAT_JOYSTICK, OPTITEM_JOYSTICK_DEVICE); UpdateControls(hPage); return FALSE; @@ -1276,7 +1291,7 @@ BOOL OptionsPage_Joystick::OnHScroll(HWND hPage, WPARAM wParam, LPARAM lParam) case SB_LINELEFT: case SB_LINERIGHT: val = HIWORD(wParam); - Cfg()->CfgJoystickPrm.ThrottleSaturation = val * 10; + Cfg()->CfgJoystickPrm.ThrottleSaturation = val; UpdateControls(hPage); g_pOrbiter->OnOptionChanged(OPTCAT_JOYSTICK, OPTITEM_JOYSTICK_PARAM); return 0; @@ -1288,7 +1303,7 @@ BOOL OptionsPage_Joystick::OnHScroll(HWND hPage, WPARAM wParam, LPARAM lParam) case SB_LINELEFT: case SB_LINERIGHT: val = HIWORD(wParam); - Cfg()->CfgJoystickPrm.Deadzone = val * 10; + Cfg()->CfgJoystickPrm.Deadzone = val; UpdateControls(hPage); g_pOrbiter->OnOptionChanged(OPTCAT_JOYSTICK, OPTITEM_JOYSTICK_PARAM); return 0; diff --git a/Src/Orbiter/OptionsPages.h b/Src/Orbiter/OptionsPages.h index 8e44fb7eb..76bd45714 100644 --- a/Src/Orbiter/OptionsPages.h +++ b/Src/Orbiter/OptionsPages.h @@ -17,6 +17,7 @@ #include #include "CustomControls.h" #include "OrbiterAPI.h" +#include class OptionsPage; class Config; @@ -326,6 +327,7 @@ class OptionsPage_UI : public OptionsPage { class OptionsPage_Joystick : public OptionsPage { public: OptionsPage_Joystick(OptionsPageContainer* container); + ~OptionsPage_Joystick() override; int ResourceId() const; const char* Name() const; const HELPCONTEXT* HelpContext() const; @@ -335,6 +337,10 @@ class OptionsPage_Joystick : public OptionsPage { BOOL OnInitDialog(HWND hPage, WPARAM wParam, LPARAM lParam); BOOL OnCommand(HWND hPage, WORD ctrlId, WORD notification, HWND hCtrl); BOOL OnHScroll(HWND hPage, WPARAM wParam, LPARAM lParam); + +private: + SDL_JoystickID* m_joylist; + int m_njoy; }; /************************************************************************ diff --git a/Src/Orbiter/Orbiter.cfg.in b/Src/Orbiter/Orbiter.cfg.in index c2fa0a009..d75343db8 100644 --- a/Src/Orbiter/Orbiter.cfg.in +++ b/Src/Orbiter/Orbiter.cfg.in @@ -3,3 +3,7 @@ EchoAllParams = FALSE ; === Subdirectory locations PlanetTexDir = ${ORBITER_PLANET_TEXTURE_INSTALL_DIR_W} + +; === Font parameters === +ImGui_FontFile = Roboto-Medium.ttf +ImGui_FontSize = 14 diff --git a/Src/Orbiter/Orbiter.cpp b/Src/Orbiter/Orbiter.cpp index 966aab155..879ac4242 100644 --- a/Src/Orbiter/Orbiter.cpp +++ b/Src/Orbiter/Orbiter.cpp @@ -7,48 +7,57 @@ // Enable visual styles. Source: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773175(v=vs.85).aspx #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") -#include -#include +// causes some collisions with Keymap.h +#define SDL_DISABLE_OLD_NAMES +#undef SDL_MAIN_USE_CALLBACKS +#include +#include +#include #include -#include #include -#include -#include "cmdline.h" -#include "D3d7util.h" -#include "D3dmath.h" -#include "Log.h" -#include "console_ng.h" -#include "State.h" + #include "Astro.h" +#include "Base.h" #include "Camera.h" -#include "Pane.h" -#include "Select.h" +#include "ConsoleManager.h" +#include "CustomControls.h" +#include "D3d7util.h" +#include "D3dmath.h" +#include "DialogWin.h" +#include "Dialogs.h" +#include "DlgCtrl.h" +#include "DlgHelp.h" // temporary #include "DlgMgr.h" -#include "Psys.h" -#include "Base.h" -#include "Vessel.h" -#include "resource.h" -#include "Orbiter.h" +#include "GraphicsAPI.h" +#include "Help.h" #include "Launchpad.h" +#include "Log.h" +#include "Memstat.h" #include "MenuInfoBar.h" -#include "Dialogs.h" -#include "DialogWin.h" +#include "Orbiter.h" +#include "Pane.h" +#include "Psys.h" #include "Script.h" -#include "Memstat.h" -#include "CustomControls.h" -#include "Help.h" +#include "Select.h" +#include "State.h" #include "Util.h" -#include "DlgHelp.h" // temporary +#include "Vessel.h" +#include "about.hpp" +#include "cmdline.h" +#include "console_ng.h" #include "htmlctrl.h" -#include "DlgCtrl.h" -#include "GraphicsAPI.h" -#include "ConsoleManager.h" +#include "imgui.h" +#include "resource.h" + +#include #include namespace fs = std::filesystem; using namespace std; using namespace oapi; +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + #define OUTPUT_DBG #define LOADSTATUSCOL 0xC08080 //0xFFD0D0 @@ -158,17 +167,13 @@ int _matherr(struct _exception *except ) return 0; } - -// ======================================================================= -// WinMain() -// Application entry containing message loop - - -INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE, LPSTR strCmdLine, INT nCmdShow) -{ +int main(int argc, char **argv) { #ifdef _CRTDBG_MAP_ALLOC _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_EVENTS); + + SDL_SetAppMetadata("OpenOrbiter", SIG7, "uk.ac.ucl.medphys.orbit"); // Verify working directory char dir[1024]; @@ -186,7 +191,7 @@ INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE, LPSTR strCmdLine, INT nCmdSh g_pOrbiter = new Orbiter; // application instance // Parse command line - orbiter::CommandLine::Parse(g_pOrbiter, strCmdLine); + orbiter::CommandLine::Parse(g_pOrbiter, argc, const_cast(argv)); // Initialise the log INITLOG("Orbiter.log", g_pOrbiter->Cfg()->CfgCmdlinePrm.bAppendLog); // init log file @@ -201,14 +206,14 @@ INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE, LPSTR strCmdLine, INT nCmdSh srand(12345); LOGOUT("Timer precision: %g sec", fine_counter_step); + auto hInstance = GetModuleHandle(nullptr); + oapiRegisterCustomControls(hInstance); - HRESULT hr; // Create application - if (FAILED (hr = g_pOrbiter->Create (hInstance))) { + if (!g_pOrbiter->Create()) { LOGOUT("Application creation failed"); - MessageBox (NULL, "Application creation failed!\nTerminating.", - "Orbiter Error", MB_OK | MB_ICONERROR); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Orbiter Error", "Application creation failed!\nTerminating.", NULL); return 0; } @@ -216,6 +221,7 @@ INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE, LPSTR strCmdLine, INT nCmdSh g_pOrbiter->Run (); delete g_pOrbiter; + SDL_Quit(); return 0; } @@ -295,6 +301,7 @@ Orbiter::Orbiter () fine_counter_step = 1.0 / freq; } + hInstance = GetModuleHandle(nullptr); pDI = new DInput(this); TRACENEW pConfig = new Config; TRACENEW pState = NULL; @@ -324,6 +331,7 @@ Orbiter::Orbiter () bFastExit = false; bRoughType = false; bStartVideoTab = false; + bShouldQuit = false; //lstatus.bkgDC = 0; cfglen = 0; ncustomcmd = 0; @@ -353,46 +361,39 @@ Orbiter::~Orbiter () // Name: Create() // Desc: This method selects a D3D device //----------------------------------------------------------------------------- -HRESULT Orbiter::Create (HINSTANCE hInstance) +bool Orbiter::Create() { - if (m_pLaunchpad) return S_OK; // already created + if (m_pLaunchpad) return true; // already created - HRESULT hr; - WNDCLASS wndClass; + HRESULT hr; + WNDCLASS wndClass; - // Enable tab controls - InitCommonControls(); - LoadLibrary ("riched20.dll"); + // Enable tab controls + InitCommonControls(); + LoadLibrary ("riched20.dll"); - // parameter manager - parses from master config file - hInst = hInstance; pConfig->Load(MasterConfigFile); strcpy (cfgpath, pConfig->CfgDirPrm.ConfigDir); cfglen = strlen (cfgpath); - if (FAILED (hr = pDI->Create (hInstance))) return hr; - - // validate configuration - if (pConfig->CfgJoystickPrm.Joy_idx > GetDInput()->NumJoysticks()) pConfig->CfgJoystickPrm.Joy_idx = 0; - // Read key mapping from file (or write default keymap) if (!keymap.Read ("keymap.cfg")) keymap.Write ("keymap.cfg"); pState = new State(); TRACENEW - // Register main dialog window class - GetClassInfo (hInstance, "#32770", &wndClass); // override default dialog class - wndClass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_MAIN_ICON)); - RegisterClass (&wndClass); + // Register main dialog window class + GetClassInfo (hInstance, "#32770", &wndClass); // override default dialog class + wndClass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_MAIN_ICON)); + RegisterClass (&wndClass); - // Find out if we are running under Linux/WINE - HKEY key; - long ret = RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Software\\Wine"), 0, KEY_QUERY_VALUE, &key); - RegCloseKey (key); - bWINEenv = (ret == ERROR_SUCCESS); + // Find out if we are running under Linux/WINE + HKEY key; + long ret = RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Software\\Wine"), 0, KEY_QUERY_VALUE, &key); + RegCloseKey (key); + bWINEenv = (ret == ERROR_SUCCESS); - // Register HTML viewer class - RegisterHtmlCtrl (hInstance, UseHtmlInline()); - CustomCtrl::RegisterClass (hInstance); + // Register HTML viewer class + RegisterHtmlCtrl (hInstance, UseHtmlInline()); + CustomCtrl::RegisterClass (hInstance); if (pConfig->CfgCmdlinePrm.bFastExit) SetFastExit(true); @@ -432,7 +433,7 @@ HRESULT Orbiter::Create (HINSTANCE hInstance) memstat = new MemStat; - return S_OK; + return true; } //----------------------------------------------------------------------------- @@ -473,7 +474,7 @@ VOID Orbiter::CloseApp (bool fast_shutdown) delete []customcmd; customcmd = NULL; } - oapiUnregisterCustomControls (hInst); + oapiUnregisterCustomControls (hInstance); } timeEndPeriod (1); } @@ -662,7 +663,7 @@ VOID Orbiter::Launch (const char *scenario) LOGOUT_ERR ("Scenario not found: %s", scenario); TerminateOnError(); } - DlgHelp::SetScenarioHelp (pState->ScnHelp()); + //DlgHelp::SetScenarioHelp (pState->ScnHelp()); long m0 = memstat->HeapUsage(); CreateRenderWindow (pConfig, scenario); @@ -674,7 +675,8 @@ VOID Orbiter::Launch (const char *scenario) // Name: CreateRenderWindow() // Desc: Create the window used for rendering the scene //----------------------------------------------------------------------------- -HWND Orbiter::CreateRenderWindow (Config *pCfg, const char *scenario) +std::shared_ptr +Orbiter::CreateRenderWindow(Config *pCfg, const char* scenario) { DWORD i; @@ -687,27 +689,39 @@ HWND Orbiter::CreateRenderWindow (Config *pCfg, const char *scenario) if (gclient) { if(pState->SplashScreen()) gclient->clbkSetSplashScreen(pState->SplashScreen(), pState->SplashColor()); - hRenderWnd = gclient->InitRenderWnd (gclient->clbkCreateRenderWindow()); + hRenderWnd = std::move(gclient->clbkCreateRenderWindow()); + gclient->InitRenderWnd(hRenderWnd); GetRenderParameters (); } else { - hRenderWnd = NULL; + hRenderWnd = nullptr; m_pConsole = new orbiter::ConsoleNG(this); } - pDI->SetRenderWindow(hRenderWnd); - if (hRenderWnd) { - bActive = true; + pDI->SetRenderWindow(hRenderWnd->Win32Handle()); + bActive = true; - // Create keyboard device - if (!pDI->CreateKbdDevice ()) { - CloseSession (); - return 0; - } + // Create joystick device + if (pDI->CreateJoyDevice ()) + plZ4 = 1; // invalidate + } - // Create joystick device - if (pDI->CreateJoyDevice ()) - plZ4 = 1; // invalidate + if (gclient) { + // GDI resources - NOT VALID FOR ALL CLIENTS! + InitializeGDIResources (hRenderWnd->Win32Handle()); + pDlgMgr = new DialogManager (this, hRenderWnd->Win32Handle()); + + // global dialog resources + g_select = new Select(); TRACENEW + pDlgMgr->AddEntry(g_select); + g_input = new InputBox(); TRACENEW + pDlgMgr->AddEntry(g_input); + + // playback screen annotation manager + snote_playback = gclient->clbkCreateAnnotation (); + } + else { + pDlgMgr = new DialogManager(this, m_pConsole->WindowHandle()); } // read simulation environment state @@ -753,23 +767,6 @@ HWND Orbiter::CreateRenderWindow (Config *pCfg, const char *scenario) } LOGOUT ("Finished initialising camera"); - if (gclient) { - // GDI resources - NOT VALID FOR ALL CLIENTS! - InitializeGDIResources (hRenderWnd); - pDlgMgr = new DialogManager (this, hRenderWnd); - - // global dialog resources - InlineDialog::GlobalInit (gclient); - g_select = new Select (gclient, hRenderWnd); TRACENEW - g_input = new InputBox (gclient, hRenderWnd, 256); TRACENEW - - // playback screen annotation manager - snote_playback = gclient->clbkCreateAnnotation (); - } - else { - pDlgMgr = new DialogManager(this, m_pConsole->WindowHandle()); - } - bSession = true; bVisible = (hRenderWnd != NULL); bRunning = bRequestRunning = true; @@ -839,9 +836,9 @@ HWND Orbiter::CreateRenderWindow (Config *pCfg, const char *scenario) // suppress throttle update on launch if (pDI->joyprop.bThrottle && pCfg->CfgJoystickPrm.bThrottleIgnore) { - DIJOYSTATE2 js; + JoyState js; if (pDI->PollJoystick(&js)) - plZ4 = *(long*)(((BYTE*)&js) + pDI->joyprop.ThrottleOfs) >> 3; + plZ4 = js.throttle; } return hRenderWnd; @@ -852,8 +849,12 @@ void Orbiter::PreCloseSession() // DEBUG if (pDlgMgr) { pDlgMgr->Clear(); } - if (gclient && pConfig->CfgDebugPrm.bSaveExitScreen) + if (gclient && pConfig->CfgDebugPrm.bSaveExitScreen) { + // Render the scene once without the ImGui dialogs shown + // so they don't appear on the preview + Render3DEnvironment(true); gclient->clbkSaveSurfaceToImage (0, "Images\\CurrentState", oapi::IMAGE_JPG); + } } //----------------------------------------------------------------------------- @@ -897,7 +898,6 @@ void Orbiter::CloseSession () snote = NULL; nsnote = 0; } - InlineDialog::GlobalExit (gclient); if (g_input) { delete g_input; g_input = 0; } if (g_select) { delete g_select; g_select = 0; } @@ -964,18 +964,21 @@ void Orbiter::GetRenderParameters () void Orbiter::BroadcastGlobalInit () { Instrument::GlobalInit (gclient); - DlgMap::GlobalInit(); } // ======================================================================= // Render3DEnvironment() // Draws the scene -HRESULT Orbiter::Render3DEnvironment () +HRESULT Orbiter::Render3DEnvironment (bool hidedialogs) { if (gclient) { + if(!hidedialogs) + pDlgMgr->ImGuiNewFrame(); gclient->clbkRenderScene (); Output2DData (); + if(!hidedialogs) + gclient->clbkImGuiRenderDrawData(); gclient->clbkDisplayFrame (); } return S_OK; @@ -987,74 +990,75 @@ HRESULT Orbiter::Render3DEnvironment () //----------------------------------------------------------------------------- void Orbiter::ScreenToClient (POINT *pt) const { - if (!IsFullscreen() && hRenderWnd) - ::ScreenToClient (hRenderWnd, pt); + // if (!IsFullscreen() && hRenderWnd) + // ::ScreenToClient (hRenderWnd, pt); } //----------------------------------------------------------------------------- // Name: Run() // Desc: Message-processing loop. Idle time is used to render the scene. //----------------------------------------------------------------------------- -INT Orbiter::Run () -{ - // Recieve and process Windows messages - BOOL bGotMsg, bCanRender, bpCanRender = TRUE; - MSG msg; - PeekMessage (&msg, NULL, 0U, 0U, PM_NOREMOVE); - - if (!pConfig->CfgCmdlinePrm.LaunchScenario.empty()) - Launch (pConfig->CfgCmdlinePrm.LaunchScenario.c_str()); - // otherwise wait for the user to make a selection from the scenario - // list in the launchpad dialog - - while (WM_QUIT != msg.message) { - - // Use PeekMessage() if the app is active, so we can use idle time to - // render the scene. Else, use GetMessage() to avoid eating CPU time. - if (bSession) { - bGotMsg = PeekMessage (&msg, NULL, 0U, 0U, PM_REMOVE); - } else { - bGotMsg = GetMessage (&msg, NULL, 0U, 0U); - } - if (bGotMsg) { - if (!m_pLaunchpad || !m_pLaunchpad->ConsumeMessage(&msg)) { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - } else { - if (bSession) { - if (bAllowInput) bActive = true, bAllowInput = false; - if (BeginTimeStep (bRunning)) { - UpdateWorld(); - EndTimeStep (bRunning); - if (bVisible) { - if (bActive) UserInput (); - bRenderOnce = TRUE; - } - if (bRunning && bCapture) { - CaptureVideoFrame (); - } - } - if (m_pConsole) - m_pConsole->ParseCmd(); - } +void Orbiter::Run() { + if (!pConfig->CfgCmdlinePrm.LaunchScenario.empty()) + Launch(pConfig->CfgCmdlinePrm.LaunchScenario.c_str()); + + SDL_Event event = {}; + bool bpCanRender = true; + while (!ShouldQuit()) { + while (SDL_PollEvent(&event)) { + if (gclient && hRenderWnd != nullptr) { + bool consumed = pDlgMgr->ConsumeEvent(event, bShouldQuit); + consumed = consumed || pDI->ConsumeEvent(event); + consumed = consumed || gclient->RenderWndProc(event, bShouldQuit); + } + if (event.type == SDL_EVENT_QUIT) { + bShouldQuit = true; + } + if (bShouldQuit) { + break; + } + } + if (bSession) { + bActive = hRenderWnd != nullptr && bVisible && + (SDL_GetKeyboardFocus() == hRenderWnd->Inner()); + if (bAllowInput) + bActive = true, bAllowInput = false; + if (BeginTimeStep(bRunning)) { + UpdateWorld(); + EndTimeStep(bRunning); + if (bVisible) { + if (bActive) + UserInput(); + bRenderOnce = true; + } + if (bRunning && bCapture) { + CaptureVideoFrame(); + } + } + if (m_pConsole) + m_pConsole->ParseCmd(); + } + + if (bRenderOnce && bVisible) { + if (FAILED(Render3DEnvironment())) { + g_pOrbiter->CloseSession(); + } + bRenderOnce = false; } - if (bRenderOnce && bVisible) { - if (FAILED (Render3DEnvironment ())) - if (hRenderWnd) DestroyWindow (hRenderWnd); - bRenderOnce = FALSE; - } - if (bSession) { - bCanRender = TRUE; - if (bCanRender && !bpCanRender) - RestoreDeviceObjects (); - bpCanRender = bCanRender; - } else - bpCanRender = TRUE; + if (bSession) { + bool bCanRender = true; + if (bCanRender && !bpCanRender) + RestoreDeviceObjects(); + bpCanRender = bCanRender; + } else { + bpCanRender = true; + } + } + if (bSession) { + PreCloseSession(); + CloseSession(); } - hRenderWnd = NULL; - return msg.wParam; } void Orbiter::SingleFrame () @@ -1075,7 +1079,7 @@ void Orbiter::SingleFrame () void Orbiter::TerminateOnError () { LogOut (">>> TERMINATING <<<"); - if (hRenderWnd) ShowWindow (hRenderWnd, FALSE); + if (hRenderWnd) SDL_HideWindow(hRenderWnd->Inner()); MessageBox (NULL, "Terminating after critical error. See Orbiter.log for details.", "Orbiter: Critical Error", MB_OK | MB_ICONERROR); @@ -1105,40 +1109,20 @@ void Orbiter::InitRotationMode () { bKeepFocus = true; - // Checks if the cursor is already hidden - if (g_iCursorShowCount == 0) { - g_iCursorShowCount = ShowCursor(FALSE); - } - - SetCapture (hRenderWnd); - - // Limit cursor to render window confines, so we don't miss the button up event - if (!bFullscreen && hRenderWnd) { - RECT rClient; - GetClientRect (hRenderWnd, &rClient); - POINT pLeftTop = {rClient.left, rClient.top}; - POINT pRightBottom = {rClient.right, rClient.bottom}; - ClientToScreen (hRenderWnd, &pLeftTop); - ClientToScreen (hRenderWnd, &pRightBottom); - RECT rScreen = {pLeftTop.x, pLeftTop.y, pRightBottom.x, pRightBottom.y}; - ClipCursor (&rScreen); - } + if (hRenderWnd) { + SDL_SetWindowRelativeMouseMode(hRenderWnd->Inner(), true); + SDL_CaptureMouse(true); + } } void Orbiter::ExitRotationMode () { bKeepFocus = false; - ReleaseCapture (); - - // Checks if the cursor is already hidden - if (g_iCursorShowCount < 0) { - g_iCursorShowCount = ShowCursor (TRUE); - } - // Release cursor from render window confines - if (!bFullscreen && hRenderWnd) { - ClipCursor (NULL); - } + if (hRenderWnd) { + SDL_SetWindowRelativeMouseMode(hRenderWnd->Inner(), false); + SDL_CaptureMouse(false); + } } void Orbiter::OnOptionChanged(DWORD cat, DWORD item) @@ -1203,7 +1187,7 @@ Vessel *Orbiter::SetFocusObject (Vessel *vessel, bool setview) it->pModule->clbkFocusChanged(g_focusobj, g_pfocusobj); if (pDlgMgr) pDlgMgr->BroadcastMessage (MSG_FOCUSVESSEL, vessel); - DlgHelp::SetVesselHelp (g_focusobj->HelpContext()); + //DlgHelp::SetVesselHelp (g_focusobj->HelpContext()); return g_pfocusobj; } @@ -1339,8 +1323,6 @@ void Orbiter::SetWarpFactor (double warp, bool force, double delay) if (fabs (warp-td.Warp()) > EPS) { td.SetWarp (warp, delay); if (td.WarpChanged()) ApplyWarpFactor(); - DlgTacc *pDlg = (pDlgMgr ? pDlgMgr->EntryExists (hInst) : NULL); - if (pDlg) pDlg->RegisterWarp(pDlg->GetHwnd(), warp, false, true, true); if (bRecord && pConfig->CfgRecPlayPrm.bRecordWarp) { char cbuf[256]; if (delay) sprintf (cbuf, "%f %f", warp, delay); @@ -1409,7 +1391,7 @@ VOID Orbiter::SetFOV (double fov, bool limit_range) // update Camera dialog HWND hCamDlg; - if (pDlgMgr && (hCamDlg = pDlgMgr->IsEntry (hInst, IDD_CAMERA))) + if (pDlgMgr && (hCamDlg = pDlgMgr->IsEntry (hInstance, IDD_CAMERA))) SendMessage (hCamDlg, WM_APP, 0, (LPARAM)&fov); } @@ -1426,7 +1408,7 @@ VOID Orbiter::IncFOV (double dfov) // update Camera dialog HWND hCamDlg; - if (pDlgMgr && (hCamDlg = pDlgMgr->IsEntry (hInst, IDD_CAMERA))) + if (pDlgMgr && (hCamDlg = pDlgMgr->IsEntry (hInstance, IDD_CAMERA))) SendMessage (hCamDlg, WM_APP, 0, (LPARAM)&fov); } @@ -1473,7 +1455,10 @@ VOID Orbiter::Quicksave () for (i = strlen(ScenarioName)-1; i > 0; i--) if (ScenarioName[i-1] == '\\') break; sprintf (fname, "Quicksave\\%s %04d", ScenarioName+i, ++g_qsaveid); - SaveScenario (fname, desc, 0); + if(SaveScenario (fname, desc, 0)) + oapiAddNotification(OAPINOTIF_SUCCESS, "Scenario saved successfully", fname); + else + oapiAddNotification(OAPINOTIF_ERROR, "Failed to save scenario", fname); } //----------------------------------------------------------------------------- @@ -1501,7 +1486,7 @@ void Orbiter::TogglePlanetariumMode() plnFlag ^= PLN_ENABLE; if (pDlgMgr) { - DlgOptions* dlg = pDlgMgr->EntryExists(hInst); + DlgOptions* dlg = pDlgMgr->EntryExists(hInstance); if (dlg) dlg->Update(); } @@ -1515,7 +1500,7 @@ void Orbiter::ToggleLabelDisplay() mkrFlag ^= MKR_ENABLE; if (pDlgMgr) { - DlgOptions* dlg = pDlgMgr->EntryExists(hInst); + DlgOptions* dlg = pDlgMgr->EntryExists(hInstance); if (dlg) dlg->Update(); } } @@ -1541,11 +1526,11 @@ const char *Orbiter::GetDefRecordName (void) const return playbackdir+i; } -void Orbiter::ToggleRecorder (bool force, bool append) +bool Orbiter::ToggleRecorder (bool force, bool append) { - if (bPlayback) return; // don't allow recording during playback + if (bPlayback) return true; // don't allow recording during playback - DlgRecorder *pDlg = (pDlgMgr ? pDlgMgr->EntryExists (hInst) : NULL); + DlgRecorder *pDlg = (pDlgMgr ? pDlgMgr->EntryExists () : NULL); int i, n = g_psys->nVessel(); const char *sname; char cbuf[256]; @@ -1557,8 +1542,7 @@ void Orbiter::ToggleRecorder (bool force, bool append) } else sname = GetDefRecordName(); if (!append && !FRecorder_PrepareDir (sname, force)) { bStartRecorder = false; - OpenDialogEx (IDD_MSG_FRECORDER, (DLGPROC)FRecorderMsg_DlgProc, DLG_CAPTIONCLOSE); - return; + return false; } } else sname = 0; FRecorder_Activate (bStartRecorder, sname, append); @@ -1566,7 +1550,7 @@ void Orbiter::ToggleRecorder (bool force, bool append) g_psys->GetVessel(i)->FRecorder_Activate (bStartRecorder, sname, append); if (bStartRecorder) SavePlaybackScn (sname); - if (pDlg) PostMessage (pDlg->GetHwnd(), WM_USER+1, 0, 0); + return true; } void Orbiter::EndPlayback () @@ -1577,7 +1561,7 @@ void Orbiter::EndPlayback () if (snote_playback) snote_playback->ClearText(); bPlayback = false; if (pDlgMgr) { - HWND hDlg = pDlgMgr->IsEntry (hInst, IDD_RECPLAY); + HWND hDlg = pDlgMgr->IsEntry (hInstance, IDD_RECPLAY); if (hDlg) PostMessage (hDlg, WM_USER+1, 0, 0); } if (g_pane && g_pane->MIBar()) g_pane->MIBar()->SetPlayback(false); @@ -1771,8 +1755,6 @@ VOID Orbiter::Output2DData () for (DWORD i = 0; i < nsnote; i++) snote[i]->Render(); if (snote_playback && pConfig->CfgRecPlayPrm.bShowNotes) snote_playback->Render(); - if (g_select->IsActive()) g_select->Display(0/*oclient->m_pddsRenderTarget*/); - if (g_input->IsActive()) g_input->Display(0/*oclient->m_pddsRenderTarget*/); } } @@ -1872,7 +1854,7 @@ void Orbiter::EndTimeStep (bool running) // check for termination of demo mode if (SessionLimitReached()) - if (hRenderWnd) PostMessage(hRenderWnd, WM_CLOSE, 0, 0); + if (hRenderWnd) PostMessage(hRenderWnd->Win32Handle(), WM_CLOSE, 0, 0); else CloseSession(); } @@ -2018,7 +2000,10 @@ VOID Orbiter::UpdateWorld () g_bStateUpdate = false; if (!KillVessels()) // kill any vessels marked for deletion - if (hRenderWnd) DestroyWindow (hRenderWnd); + { + // FIXME(@ThePuzzlemaker): I'm not really sure what's the best way to go about this + SDL_DestroyWindow(hRenderWnd->Inner()); + } //g_texmanager->OutputInfo(); } @@ -2049,38 +2034,36 @@ HRESULT Orbiter::UserInput () if ((g_input && g_input->IsActive()) || (g_select && g_select->IsActive())) skipkbd = true; - if (didev = GetDInput()->GetKbdDevice()) { - // keyboard input: immediate key interpretation - hr = didev->GetDeviceState (sizeof(buffer), &buffer); - if ((hr == DIERR_NOTACQUIRED || hr == DIERR_INPUTLOST) && SUCCEEDED (didev->Acquire())) - hr = didev->GetDeviceState (sizeof(buffer), &buffer); - if (SUCCEEDED (hr)) - for (i = 0; i < 256; i++) - simkstate[i] |= buffer[i]; - bool consume = BroadcastImmediateKeyboardEvent (simkstate); - if (!skipkbd && !consume) { - KbdInputImmediate_System (simkstate); - if (bRunning) KbdInputImmediate_OnRunning (simkstate); + ImGuiIO& io = ImGui::GetIO(); + // keyboard input: immediate key interpretation + auto numkey = 0; + const auto kstate = SDL_GetKeyboardState(&numkey); + for (i = 0; i < numkey; i++) { + if (i >= sizeof(buffer)) break; + buffer[i] = kstate[i] ? 0x80 : 0x00; + if (!io.WantCaptureKeyboard) + simkstate[i] |= buffer[i]; + } + + bool consume = BroadcastImmediateKeyboardEvent (simkstate); + if (!skipkbd && !consume) { + KbdInputImmediate_System (simkstate); + if (bRunning) KbdInputImmediate_OnRunning (simkstate); + } + // keyboard input: buffered key events + if (!io.WantCaptureKeyboard) { + BroadcastBufferedKeyboardEvent (buffer); + if (!skipkbd) { + KbdInputBuffered_System (buffer); + if (bRunning) KbdInputBuffered_OnRunning (buffer); } - - // keyboard input: buffered key events - hr = didev->GetDeviceData (sizeof(DIDEVICEOBJECTDATA), dod, &dwItems, 0); - if ((hr == DIERR_NOTACQUIRED || hr == DIERR_INPUTLOST) && SUCCEEDED (didev->Acquire())) - hr = didev->GetDeviceData (sizeof(DIDEVICEOBJECTDATA), dod, &dwItems, 0); - if (SUCCEEDED (hr)) { - BroadcastBufferedKeyboardEvent (buffer, dod, dwItems); - if (!skipkbd) { - KbdInputBuffered_System (buffer, dod, dwItems); - if (bRunning) KbdInputBuffered_OnRunning (buffer, dod, dwItems); - } - } - //if (hr == DI_BUFFEROVERFLOW) MessageBeep (-1); + pDI->m_bufferedEvents.clear(); } for (i = 0; i < 15; i++) ctrlTotal[i] = ctrlKeyboard[i]; // update attitude requests // joystick input - DIJOYSTATE2 js; + JoyState js; if (pDI->PollJoystick (&js)) { UserJoyInput_System (&js); // general joystick functions if (bRunning) UserJoyInput_OnRunning (&js); // joystick vessel control functions @@ -2111,9 +2094,9 @@ bool Orbiter::SendKbdBuffered(DWORD key, DWORD *mod, DWORD nmod, bool onRunningO memset (buffer, 0, 256); for (int i = 0; i < nmod; i++) buffer[mod[i]] = 0x80; - BroadcastBufferedKeyboardEvent (buffer, &dod, 1); - KbdInputBuffered_System (buffer, &dod, 1); - KbdInputBuffered_OnRunning (buffer, &dod, 1); + BroadcastBufferedKeyboardEvent (buffer); + KbdInputBuffered_System (buffer); + KbdInputBuffered_OnRunning (buffer); return true; } @@ -2310,12 +2293,12 @@ void Orbiter::KbdInputImmediate_OnRunning (char *kstate) // Desc: General user keyboard buffered key interpretation. Processes keys // which are also interpreted when simulation is paused //----------------------------------------------------------------------------- -void Orbiter::KbdInputBuffered_System (char *kstate, DIDEVICEOBJECTDATA *dod, DWORD n) +void Orbiter::KbdInputBuffered_System (char *kstate) { - for (DWORD i = 0; i < n; i++) { + for (const auto& evt : pDI->m_bufferedEvents) { - if (!(dod[i].dwData & 0x80)) continue; // only process key down events - DWORD key = dod[i].dwOfs; + if (!evt.down) continue; // only process key down events + DWORD key = evt.scancode; if (keymap.IsLogicalKey(key, kstate, OAPI_LKEY_Pause)) TogglePause(); else if (keymap.IsLogicalKey(key, kstate, OAPI_LKEY_Quicksave)) Quicksave(); @@ -2340,7 +2323,7 @@ void Orbiter::KbdInputBuffered_System (char *kstate, DIDEVICEOBJECTDATA *dod, DW if (bPlayback) EndPlayback(); else ToggleRecorder (); } else if (keymap.IsLogicalKey (key, kstate, OAPI_LKEY_Quit)) { - if (hRenderWnd) PostMessage (hRenderWnd, WM_CLOSE, 0, 0); + if (hRenderWnd) PostMessage (hRenderWnd->Win32Handle(), WM_CLOSE, 0, 0); } else if (keymap.IsLogicalKey (key, kstate, OAPI_LKEY_SelectPrevVessel)) { if (g_pfocusobj) SetFocusObject (g_pfocusobj); } @@ -2369,12 +2352,11 @@ void Orbiter::KbdInputBuffered_System (char *kstate, DIDEVICEOBJECTDATA *dod, DW // Name: KbdInputBuffered_OnRunning () // Desc: User keyboard buffered key interpretation in running simulation //----------------------------------------------------------------------------- -void Orbiter::KbdInputBuffered_OnRunning (char *kstate, DIDEVICEOBJECTDATA *dod, DWORD n) +void Orbiter::KbdInputBuffered_OnRunning (char *kstate) { - for (DWORD i = 0; i < n; i++) { - - DWORD key = dod[i].dwOfs; - bool bdown = (dod[i].dwData & 0x80) != 0; + for (const auto &evt : pDI->m_bufferedEvents) { + DWORD key = evt.scancode; + bool bdown = evt.down; if (g_focusobj->ConsumeBufferedKey (key, bdown, kstate)) // offer key to vessel for processing continue; @@ -2396,7 +2378,7 @@ void Orbiter::KbdInputBuffered_OnRunning (char *kstate, DIDEVICEOBJECTDATA *dod, } else if (KEYMOD_SHIFT (kstate)) { // Shift-key combinations (reserved for MFD control) - int id = (KEYDOWN (kstate, DIK_LSHIFT) ? 0 : 1); + int id = (KEYDOWN (kstate, SDL_SCANCODE_LSHIFT) ? 0 : 1); g_pane->MFDConsumeKeyBuffered (id, key); } else if (KEYMOD_ALT (kstate)) { // ALT-Key combinations @@ -2416,33 +2398,32 @@ void Orbiter::KbdInputBuffered_OnRunning (char *kstate, DIDEVICEOBJECTDATA *dod, // Name: UserJoyInput_System () // Desc: General user joystick input (also functional when paused) //----------------------------------------------------------------------------- -void Orbiter::UserJoyInput_System (DIJOYSTATE2 *js) +void Orbiter::UserJoyInput_System (JoyState *js) { - if (LOWORD (js->rgdwPOV[0]) != 0xFFFF) { - DWORD dir = js->rgdwPOV[0]; + if (js->hat != SDL_HAT_CENTERED) { if (g_camera->IsExternal()) { // use the joystick's coolie hat to rotate external camera - if (js->rgbButtons[2]) { // shift instrument panel - if (dir < 5000 || dir > 31000) g_camera->Rotate (0, td.SysDT); - else if (dir > 13000 && dir < 23000) g_camera->Rotate (0, -td.SysDT); - if (dir > 4000 && dir < 14000) g_camera->Rotate (-td.SysDT, 0); - else if (dir > 22000 && dir < 32000) g_camera->Rotate ( td.SysDT, 0); + if (js->btn3) { // shift instrument panel + if ((js->hat & SDL_HAT_UP) != 0) g_camera->Rotate (0, td.SysDT); + else if ((js->hat & SDL_HAT_DOWN) != 0) g_camera->Rotate (0, -td.SysDT); + if ((js->hat & SDL_HAT_RIGHT) != 0) g_camera->Rotate (-td.SysDT, 0); + else if ((js->hat & SDL_HAT_LEFT) != 0) g_camera->Rotate ( td.SysDT, 0); } else { - if (dir < 5000 || dir > 31000) g_camera->AddTheta (-td.SysDT); - else if (dir > 13000 && dir < 23000) g_camera->AddTheta ( td.SysDT); - if (dir > 4000 && dir < 14000) g_camera->AddPhi ( td.SysDT); - else if (dir > 22000 && dir < 32000) g_camera->AddPhi (-td.SysDT); + if ((js->hat & SDL_HAT_UP) != 0) g_camera->AddTheta (-td.SysDT); + else if ((js->hat & SDL_HAT_DOWN) != 0) g_camera->AddTheta ( td.SysDT); + if ((js->hat & SDL_HAT_RIGHT) != 0) g_camera->AddPhi ( td.SysDT); + else if ((js->hat & SDL_HAT_LEFT) != 0) g_camera->AddPhi (-td.SysDT); } } else { // internal view - if (js->rgbButtons[2]) { // shift instrument panel - if (dir < 5000 || dir > 31000) g_pane->ShiftPanel (0.0, td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed); - else if (dir > 13000 && dir < 23000) g_pane->ShiftPanel (0.0, -td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed); - if (dir > 4000 && dir < 14000) g_pane->ShiftPanel (-td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed, 0.0); - else if (dir > 22000 && dir < 32000) g_pane->ShiftPanel ( td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed, 0.0); + if (js->btn3) { // shift instrument panel + if ((js->hat & SDL_HAT_UP) != 0) g_pane->ShiftPanel (0.0, td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed); + else if ((js->hat & SDL_HAT_DOWN) != 0) g_pane->ShiftPanel (0.0, -td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed); + if ((js->hat & SDL_HAT_RIGHT) != 0) g_pane->ShiftPanel (-td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed, 0.0); + else if ((js->hat & SDL_HAT_LEFT) != 0) g_pane->ShiftPanel ( td.SysDT*pConfig->CfgLogicPrm.PanelScrollSpeed, 0.0); } else { // rotate camera - if (dir < 5000 || dir > 31000) g_camera->Rotate (0, td.SysDT, true); - else if (dir > 13000 && dir < 23000) g_camera->Rotate (0, -td.SysDT, true); - if (dir > 4000 && dir < 14000) g_camera->Rotate (-td.SysDT, 0, true); - else if (dir > 22000 && dir < 32000) g_camera->Rotate ( td.SysDT, 0, true); + if ((js->hat & SDL_HAT_UP) != 0) g_camera->Rotate (0, td.SysDT, true); + else if ((js->hat & SDL_HAT_DOWN) != 0) g_camera->Rotate (0, -td.SysDT, true); + if ((js->hat & SDL_HAT_RIGHT) != 0) g_camera->Rotate (-td.SysDT, 0, true); + else if ((js->hat & SDL_HAT_LEFT) != 0) g_camera->Rotate ( td.SysDT, 0, true); } } } @@ -2452,36 +2433,37 @@ void Orbiter::UserJoyInput_System (DIJOYSTATE2 *js) // Name: UserJoyInput_OnRunning () // Desc: User joystick input query for running simulation (ship controls etc.) //----------------------------------------------------------------------------- -void Orbiter::UserJoyInput_OnRunning (DIJOYSTATE2 *js) +void Orbiter::UserJoyInput_OnRunning (JoyState *js) { if (bEnableAtt) { - if (js->lX) { - if (js->rgbButtons[2]) { // emulate rudder control - if (js->lX > 0) ctrlJoystick[THGROUP_ATT_YAWRIGHT] = js->lX; - else ctrlJoystick[THGROUP_ATT_YAWLEFT] = -js->lX; + if (js->xAx) { + if (js->btn3) { // emulate rudder control + if (js->xAx > 0) ctrlJoystick[THGROUP_ATT_YAWRIGHT] = js->xAx; + else ctrlJoystick[THGROUP_ATT_YAWLEFT] = -js->xAx; } else { // rotation (bank) - if (js->lX > 0) ctrlJoystick[THGROUP_ATT_BANKRIGHT] = js->lX; - else ctrlJoystick[THGROUP_ATT_BANKLEFT] = -js->lX; + if (js->xAx > 0) ctrlJoystick[THGROUP_ATT_BANKRIGHT] = js->xAx; + else ctrlJoystick[THGROUP_ATT_BANKLEFT] = -js->xAx; } } - if (js->lY) { // rotation (pitch) or translation (vertical) - if (js->lY > 0) ctrlJoystick[THGROUP_ATT_PITCHUP] = ctrlJoystick[THGROUP_ATT_UP] = js->lY; - else ctrlJoystick[THGROUP_ATT_PITCHDOWN] = ctrlJoystick[THGROUP_ATT_DOWN] = -js->lY; + if (js->yAx) { // rotation (pitch) or translation (vertical) + if (js->yAx > 0) ctrlJoystick[THGROUP_ATT_PITCHUP] = ctrlJoystick[THGROUP_ATT_UP] = js->yAx; + else ctrlJoystick[THGROUP_ATT_PITCHDOWN] = ctrlJoystick[THGROUP_ATT_DOWN] = -js->yAx; } - if (js->lRz) { // rotation (yaw) or translation (transversal) - if (js->lRz > 0) ctrlJoystick[THGROUP_ATT_YAWRIGHT] = ctrlJoystick[THGROUP_ATT_RIGHT] = js->lRz; - else ctrlJoystick[THGROUP_ATT_YAWLEFT] = ctrlJoystick[THGROUP_ATT_LEFT] = -js->lRz; + if (js->zRot) { // rotation (yaw) or translation (transversal) + if (js->zRot > 0) ctrlJoystick[THGROUP_ATT_YAWRIGHT] = ctrlJoystick[THGROUP_ATT_RIGHT] = js->zRot; + else ctrlJoystick[THGROUP_ATT_YAWLEFT] = ctrlJoystick[THGROUP_ATT_LEFT] = -js->zRot; } } if (pDI->joyprop.bThrottle) { // main thrusters via throttle control - long lZ4 = *(long*)(((BYTE*)js)+pDI->joyprop.ThrottleOfs) >> 3; + long lZ4 = js->throttle; if (lZ4 != plZ4) { if (ignorefirst) { - if (abs(lZ4-plZ4) > 10) ignorefirst = false; + if (abs(lZ4-plZ4) > 128) ignorefirst = false; else return; } - double th = -0.008 * (plZ4 = lZ4); + // Map [-32768, 32767] to [0, 1] + double th = -((plZ4 = lZ4) - 32768) / 65536.0; if (th > 1.0) th = 1.0; g_focusobj->SetThrusterGroupLevel (THGROUP_MAIN, th); g_focusobj->SetThrusterGroupLevel (THGROUP_RETRO, 0.0); @@ -2489,36 +2471,32 @@ void Orbiter::UserJoyInput_OnRunning (DIJOYSTATE2 *js) } } -bool Orbiter::MouseEvent (UINT event, DWORD state, DWORD x, DWORD y) +bool Orbiter::MouseEvent (const SDL_Event &event, DWORD x, DWORD y) { // Prioritizes mouse handling while in rotation mode if (g_pOrbiter->StickyFocus()) { - if (event == WM_MOUSEMOVE) return false; // may be lifted later - if (g_camera->ProcessMouse(event, state, x, y, simkstate)) return true; + if (event.type == SDL_EVENT_MOUSE_MOTION) return false; // may be lifted later + if (g_camera->ProcessMouse(event, x, y, simkstate)) return true; } - if (g_pane->MIBar() && g_pane->MIBar()->ProcessMouse (event, state, x, y)) return true; - if (BroadcastMouseEvent (event, state, x, y)) return true; - if (event == WM_MOUSEMOVE) return false; // may be lifted later + if (g_pane->MIBar() && g_pane->MIBar()->ProcessMouse (event, x, y)) return true; + if (BroadcastMouseEvent (event, x, y)) return true; + if (event.type == SDL_EVENT_MOUSE_MOTION) return false; // may be lifted later if (bRunning) { - if (event == WM_LBUTTONDOWN || event == WM_RBUTTONDOWN) { - if (g_input && g_input->IsActive()) g_input->Close(); - if (g_select && g_select->IsActive()) g_select->Clear (true); - } - if (g_pane->ProcessMouse_OnRunning (event, state, x, y, simkstate)) return true; + if (g_pane->ProcessMouse_OnRunning (event, x, y, simkstate)) return true; } - if (g_pane->ProcessMouse_System(event, state, x, y, simkstate)) return true; - if (g_camera->ProcessMouse (event, state, x, y, simkstate)) return true; + if (g_pane->ProcessMouse_System(event, x, y, simkstate)) return true; + if (g_camera->ProcessMouse (event, x, y, simkstate)) return true; return false; } -bool Orbiter::BroadcastMouseEvent (UINT event, DWORD state, DWORD x, DWORD y) +bool Orbiter::BroadcastMouseEvent (const SDL_Event &event, DWORD x, DWORD y) { bool consume = false; for (auto it = m_Plugin.begin(); it != m_Plugin.end(); it++) - if (it->pModule && it->pModule->clbkProcessMouse(event, state, x, y)) + if (it->pModule && it->pModule->clbkProcessMouse(event, x, y)) consume = true; return consume; @@ -2535,18 +2513,28 @@ bool Orbiter::BroadcastImmediateKeyboardEvent (char *kstate) return consume; } -void Orbiter::BroadcastBufferedKeyboardEvent (char *kstate, DIDEVICEOBJECTDATA *dod, DWORD n) +void Orbiter::BroadcastBufferedKeyboardEvent (char *kstate) { - for (DWORD i = 0; i < n; i++) { + auto evt = pDI->m_bufferedEvents.begin(); + while (evt != pDI->m_bufferedEvents.end()) { bool consume = false; - if (!(dod[i].dwData & 0x80)) continue; // only process key down events - DWORD key = dod[i].dwOfs; + if (!evt->down) { + // only process key down events + ++evt; + continue; + } + DWORD key = evt->scancode; for (auto it = m_Plugin.begin(); it != m_Plugin.end(); it++) if (it->pModule && it->pModule->clbkProcessKeyboardBuffered(key, kstate, bRunning)) consume = true; - if (consume) dod[i].dwData = 0; // remove key from process queue + if (consume) { + // remove key from process queue + evt = pDI->m_bufferedEvents.erase(evt); + } else { + ++evt; + } } } @@ -2555,150 +2543,19 @@ void Orbiter::BroadcastBufferedKeyboardEvent (char *kstate, DIDEVICEOBJECTDATA * // Desc: Render window message handler //----------------------------------------------------------------------------- -LRESULT Orbiter::MsgProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +bool Orbiter::MsgProc (const SDL_Event &event, bool &wantsOut) { - WORD kmod; - - switch (uMsg) { - - case WM_ACTIVATE: - bActive = (wParam != WA_INACTIVE); - return 0; - - case WM_CHAR: - // make dialogs modal to avoid complications - if (g_input && g_input->IsActive()) { - if (g_input->ConsumeKey (uMsg, wParam) != Select::key_ignore) bRenderOnce = TRUE; - return 0; - } - if (g_select && g_select->IsActive()) { - if (g_select->ConsumeKey (uMsg, wParam) != Select::key_ignore) bRenderOnce = TRUE; - return 0; - } - break; - - // *** User Keyboard Input *** - case WM_KEYDOWN: - - // modifiers - kmod = 0; - if (GetKeyState (VK_SHIFT) & 0x8000) kmod |= 0x01; - if (GetKeyState (VK_CONTROL) & 0x8000) kmod |= 0x02; - - // make dialogs modal to avoid complications - if (g_input && g_input->IsActive()) { - if (g_input->ConsumeKey (uMsg, wParam, kmod) != Select::key_ignore) bRenderOnce = TRUE; - return 0; - } - if (g_select && g_select->IsActive()) { - if (g_select->ConsumeKey (uMsg, wParam, kmod) != Select::key_ignore) bRenderOnce = TRUE; - return 0; - } - break; - - // Mouse event handler - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONUP: { - if (MouseEvent(uMsg, wParam, LOWORD(lParam), HIWORD(lParam))) - break; //return 0; - } break; - case WM_MOUSEWHEEL: { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - if (!bFullscreen) { - POINT pt = { x, y }; - ScreenToClient(&pt); // for some reason this message passes screen coordinates - x = pt.x; - y = pt.y; - } - if (MouseEvent(uMsg, wParam, x, y)) - break; //return 0; - } break; - case WM_MOUSEMOVE: { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - MouseEvent(uMsg, wParam, x, y); - if (!bKeepFocus && pConfig->CfgUIPrm.MouseFocusMode != 0 && GetFocus() != hWnd) { - if (GetWindowThreadProcessId(hWnd, NULL) == GetWindowThreadProcessId(GetFocus(), NULL)) - SetFocus(hWnd); - } - }return 0; - -#ifdef UNDEF - // These messages could be intercepted to suspend the simulation - // during resizing and menu operations. Not a good idea for real-time - // applications though - case WM_ENTERMENULOOP: // Pause the app when menus are displayed - Pause (TRUE); - break; - - case WM_EXITMENULOOP: // Resume when menu is closed - Pause (FALSE); - break; - - case WM_ENTERSIZEMOVE: // Pause during resizing or moving - if (m_bRunning) Suspend (); - break; - - case WM_EXITSIZEMOVE: // Resume after resizing or moving - if (m_bRunning) Resume (); - break; -#endif - - case WM_GETMINMAXINFO: - ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100; - ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100; - break; - - case WM_POWERBROADCAST: - switch (wParam) { - case PBT_APMQUERYSUSPEND: - // At this point, the app should save any data for open - // network connections, files, etc.., and prepare to go into - // a suspended mode. - Freeze (true); - return TRUE; - - case PBT_APMRESUMESUSPEND: - // At this point, the app should recover any data, network - // connections, files, etc.., and resume running from when - // the app was suspended. - Freeze (false); - return TRUE; - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case SC_MONITORPOWER: - // Prevent potential crashes when the monitor powers down - return 1; - - case IDM_EXIT: - // Recieved key/menu command to exit render window - SendMessage (hWnd, WM_CLOSE, 0, 0); - return 0; - } - break; - - case WM_NCHITTEST: - // Prevent the user from selecting the menu in fullscreen mode - if (IsFullscreen()) return HTCLIENT; - break; - - // shutdown options - case WM_CLOSE: - PreCloseSession(); - DestroyWindow (hWnd); - return 0; - - case WM_DESTROY: - CloseSession (); - break; - } - return DefWindowProc (hWnd, uMsg, wParam, lParam); + if (event.type == SDL_EVENT_MOUSE_BUTTON_UP || event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { + if (MouseEvent(event, static_cast(event.button.x), static_cast(event.button.y))) + return true; + } else if (event.type == SDL_EVENT_MOUSE_WHEEL) { + if (MouseEvent(event, static_cast(event.wheel.mouse_x), static_cast(event.wheel.mouse_y))) + return true; + } else if (event.type == SDL_EVENT_MOUSE_MOTION) { + if (MouseEvent(event, static_cast(event.motion.x), static_cast(event.motion.y))) + return true; + } + return false; } //----------------------------------------------------------------------------- @@ -2760,7 +2617,7 @@ bool Orbiter::RemoveGraphicsClient (oapi::GraphicsClient *gc) bool Orbiter::RegisterWindow (HINSTANCE hInstance, HWND hWnd, DWORD flag) { - return (pDlgMgr ? (pDlgMgr->AddWindow (hInstance, hWnd, hRenderWnd, flag) != NULL) : NULL); + return (pDlgMgr ? (pDlgMgr->AddWindow (hInstance, hWnd, hRenderWnd->Win32Handle(), flag) != NULL) : NULL); } void Orbiter::UpdateDeallocationProgress() @@ -2770,32 +2627,30 @@ void Orbiter::UpdateDeallocationProgress() HWND Orbiter::OpenDialog (int id, DLGPROC pDlg, void *context) { - return OpenDialog (hInst, id, pDlg, context); + return OpenDialog (hInstance, id, pDlg, context); } HWND Orbiter::OpenDialogEx (int id, DLGPROC pDlg, DWORD flag, void *context) { - return OpenDialogEx (hInst, id, pDlg, flag, context); + return OpenDialogEx (hInstance, id, pDlg, flag, context); } HWND Orbiter::OpenDialog (HINSTANCE hInstance, int id, DLGPROC pDlg, void *context) { - return (pDlgMgr ? pDlgMgr->OpenDialog (hInstance, id, hRenderWnd, pDlg, context) : NULL); + return (pDlgMgr ? pDlgMgr->OpenDialog (hInstance, id, hRenderWnd->Win32Handle(), pDlg, context) : NULL); } HWND Orbiter::OpenDialogEx (HINSTANCE hInstance, int id, DLGPROC pDlg, DWORD flag, void *context) { - return (pDlgMgr ? pDlgMgr->OpenDialogEx (hInstance, id, hRenderWnd, pDlg, flag, context) : NULL); + return (pDlgMgr ? pDlgMgr->OpenDialogEx (hInstance, id, hRenderWnd->Win32Handle(), pDlg, flag, context) : NULL); } -HWND Orbiter::OpenHelp (const HELPCONTEXT *hcontext) +void Orbiter::OpenHelp (const HELPCONTEXT *hcontext) { if (pDlgMgr) { DlgHelp *pHelp = pDlgMgr->EnsureEntry (); - HWND hHelp = pHelp->GetHwnd(); - PostMessage (hHelp, WM_USER+1, 0, (LPARAM)hcontext); - return hHelp; - } else return NULL; + pHelp->OpenHelp(hcontext); + } } void Orbiter::OpenLaunchpadHelp (HELPCONTEXT *hcontext) diff --git a/Src/Orbiter/Orbiter.h b/Src/Orbiter/Orbiter.h index 8bffadca8..a57978d49 100644 --- a/Src/Orbiter/Orbiter.h +++ b/Src/Orbiter/Orbiter.h @@ -4,14 +4,16 @@ #ifndef ORBITER_H #define ORBITER_H +#include #include "Config.h" #include "Input.h" #include "Select.h" #include "Keymap.h" -#include -#include +#include + #include "Mesh.h" #include "TimeData.h" +#include class DInput; class Config; @@ -51,18 +53,18 @@ class Orbiter { Orbiter (); ~Orbiter (); - HRESULT Create (HINSTANCE); + bool Create (); VOID Launch (const char *scenario); void CloseApp (bool fast_shutdown = false); int GetVersion () const; - HWND CreateRenderWindow (Config *pCfg, const char *scenario); + std::shared_ptr CreateRenderWindow (Config *pCfg, const char *scenario); void PreCloseSession(); void CloseSession (); void GetRenderParameters (); bool InitializeWorld (char *name); void ScreenToClient (POINT *pt) const; - LRESULT MsgProc (HWND, UINT, WPARAM, LPARAM); - HRESULT Render3DEnvironment(); + bool MsgProc (const SDL_Event &event, bool &wantsOut); + HRESULT Render3DEnvironment(bool hidedialogs = false); VOID Output2DData (); void OutputLoadStatus (const char *msg, int line); void OutputLoadTick (int line, bool ok = true); @@ -72,7 +74,9 @@ class Orbiter { void ExitRotationMode (); bool StickyFocus() const { return bKeepFocus; } void OpenVideoTab() { bStartVideoTab = true; } - INT Run (); + void Run (); + void SetShouldQuit() { bShouldQuit = true; }; + bool ShouldQuit() { return bShouldQuit; } void SingleFrame (); void Pause (bool bPause); void Freeze (bool bFreeze); @@ -96,7 +100,7 @@ class Orbiter { HWND OpenDialog (HINSTANCE hInst, int id, DLGPROC pDlg, void *context = 0); // use this version for for calls from external dlls HWND OpenDialogEx (int id, DLGPROC pDlg, DWORD flag = 0, void *context = 0); // extended version HWND OpenDialogEx (HINSTANCE hInst, int id, DLGPROC pDlg, DWORD flag = 0, void *context = 0); // extended version - HWND OpenHelp (const HELPCONTEXT *hcontext); + void OpenHelp (const HELPCONTEXT *hcontext); void OpenLaunchpadHelp (HELPCONTEXT *hcontext); HELPCONTEXT DefaultHelpPage(const char* topic); //void OpenDialogAsync (int id, DLGPROC pDlg, void *context = 0); @@ -154,8 +158,8 @@ class Orbiter { // Increase camera field of view by dfov // Accessor functions - inline HINSTANCE GetInstance() const { return hInst; } - inline HWND GetRenderWnd() const { return hRenderWnd; } + inline HINSTANCE GetInstance() const { return hInstance; } + inline const std::shared_ptr& GetRenderWnd() const { return hRenderWnd; } inline bool IsFullscreen() const { return bFullscreen; } inline DWORD ViewW() const { return viewW; } inline DWORD ViewH() const { return viewH; } @@ -167,15 +171,11 @@ class Orbiter { inline State* PState() const { return pState; } inline bool IsActive() const { return bActive; } // temporary inline bool IsRunning() const { return bRunning; } + inline bool HasSession() const { return bSession; } inline bool UseStencil() const { return bUseStencil; } inline void SetFastExit (bool fexit) { bFastExit = fexit; } inline bool UseHtmlInline() { return (pConfig->CfgDebugPrm.bHtmlScnDesc == 1 || pConfig->CfgDebugPrm.bHtmlScnDesc == 2 && !bWINEenv); } - // DirectInput components - inline CDIFramework7 *GetDInput() const { return pDI->GetDIFrame(); } - inline LPDIRECTINPUTDEVICE8 GetKbdDevice() const { return pDI->GetKbdDevice(); } - inline LPDIRECTINPUTDEVICE8 GetJoyDevice() const { return pDI->GetJoyDevice(); } - // memory monitor MemStat *memstat; long simheapsize; // memory allocated during CreateRenderWindow @@ -210,7 +210,7 @@ class Orbiter { std::ifstream *FRsys_stream; // system event playback file double frec_sys_simt; // system event timer PlaybackEditor *FReditor; // playback editor instance - void ToggleRecorder (bool force = false, bool append = false); + bool ToggleRecorder (bool force = false, bool append = false); void EndPlayback (); inline int RecorderStatus() const { return (bRecord ? 1 : bPlayback ? 2 : 0); } inline bool IsPlayback() const { return bPlayback; } @@ -305,14 +305,14 @@ class Orbiter { HRESULT UserInput (); void KbdInputImmediate_System (char *kstate); void KbdInputImmediate_OnRunning (char *buffer); - void KbdInputBuffered_System (char *kstate, DIDEVICEOBJECTDATA *dod, DWORD n); - void KbdInputBuffered_OnRunning (char *kstate, DIDEVICEOBJECTDATA *dod, DWORD n); - void UserJoyInput_System (DIJOYSTATE2 *js); - void UserJoyInput_OnRunning (DIJOYSTATE2 *js); - bool MouseEvent (UINT event, DWORD state, DWORD x, DWORD y); - bool BroadcastMouseEvent (UINT event, DWORD state, DWORD x, DWORD y); + void KbdInputBuffered_System (char *kstate); + void KbdInputBuffered_OnRunning (char *kstate); + void UserJoyInput_System (JoyState *js); + void UserJoyInput_OnRunning (JoyState *js); + bool MouseEvent (const SDL_Event &event, DWORD x, DWORD y); + bool BroadcastMouseEvent (const SDL_Event &event, DWORD x, DWORD y); bool BroadcastImmediateKeyboardEvent (char *kstate); - void BroadcastBufferedKeyboardEvent (char *kstate, DIDEVICEOBJECTDATA *dod, DWORD n); + void BroadcastBufferedKeyboardEvent (char *kstate); void BroadcastGlobalInit(); @@ -351,8 +351,8 @@ class Orbiter { DialogManager *pDlgMgr; orbiter::ConsoleNG* m_pConsole; // The console window opened when Orbiter server is launched without a graphics client DInput *pDI; - HINSTANCE hInst; // orbiter instance handle - HWND hRenderWnd; // render window handle (NULL if no render support) + HINSTANCE hInstance; // orbiter instance handle + std::shared_ptr hRenderWnd; // render window handle (NULL if no render support) HWND hBk; // background window handle (demo mode only) BOOL bRenderOnce; // flag for single frame render request BOOL bEnableLighting; @@ -393,6 +393,7 @@ class Orbiter { bool bFastExit; // terminate on simulation end? bool bSysClearType; // is cleartype enabled on the user's system? bool bRoughType; // font-smoothing disabled? + bool bShouldQuit; // Manual joystick/keyboard attitude inputs DWORD ctrlJoystick[15]; diff --git a/Src/Orbiter/OrbiterAPI.cpp b/Src/Orbiter/OrbiterAPI.cpp index 78464c29e..e101bc27b 100644 --- a/Src/Orbiter/OrbiterAPI.cpp +++ b/Src/Orbiter/OrbiterAPI.cpp @@ -1966,6 +1966,14 @@ DLLEXPORT HDC oapiGetDC (SURFHANDLE surf) return hDC; } +DLLEXPORT uint64_t oapiGetImTextureID (SURFHANDLE surf) +{ + oapi::GraphicsClient *gc = g_pOrbiter->GetGraphicsClient(); + if (gc && surf) + return gc->clbkImGuiSurfaceTexture (surf); + return 0; +} + DLLEXPORT void oapiReleaseDC (SURFHANDLE surf, HDC hDC) { oapi::GraphicsClient *gc = g_pOrbiter->GetGraphicsClient(); @@ -2159,6 +2167,12 @@ DLLEXPORT HWND oapiOpenDialogEx (HINSTANCE hDLLInst, int resourceId, DLGPROC msg return g_pOrbiter->OpenDialogEx (hDLLInst, resourceId, msgProc, flag, context); } +DLLEXPORT void oapiOpenDialog(ImGuiDialog *e) +{ + g_pOrbiter->DlgMgr()->AddEntry(e); + e->Activate(); +} + DLLEXPORT HWND oapiFindDialog (HINSTANCE hDLLInst, int resourceId) { return g_pOrbiter->IsDialog (hDLLInst, resourceId); @@ -2169,6 +2183,12 @@ DLLEXPORT void oapiCloseDialog (HWND hDlg) g_pOrbiter->CloseDialog (hDlg); } +DLLEXPORT void oapiCloseDialog(ImGuiDialog *e) +{ + if (g_pOrbiter->DlgMgr()) + g_pOrbiter->DlgMgr()->DelEntry(e); +} + DLLEXPORT void *oapiGetDialogContext (HWND hDlg) { DialogManager *dlgmgr = g_pOrbiter->DlgMgr(); @@ -2205,7 +2225,7 @@ DLLEXPORT INT_PTR oapiDefDialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM DLLEXPORT bool oapiOpenHelp (HELPCONTEXT *hcontext) { - HWND hDlg = g_pOrbiter->OpenHelp (hcontext); + g_pOrbiter->OpenHelp (hcontext); return true; } diff --git a/Src/Orbiter/Pane.cpp b/Src/Orbiter/Pane.cpp index 1208ddc84..41dfc8551 100644 --- a/Src/Orbiter/Pane.cpp +++ b/Src/Orbiter/Pane.cpp @@ -27,8 +27,8 @@ static COLORREF normalColor = RGB ( 0, 255, 0); static COLORREF infoColor = RGB (224, 192, 0); static COLORREF brightColor = RGB (255, 224, 128); - -Pane::Pane (oapi::GraphicsClient *gclient, HWND hwnd, int width, int height, int bpp) + +Pane::Pane (oapi::GraphicsClient *gclient, std::shared_ptr hwnd, int width, int height, int bpp) { // Note: gclient is assumed to be a valid pointer. Nongraphics orbiter // instances should not create a Pane. @@ -145,29 +145,31 @@ bool Pane::MFDConsumeKeyBuffered (int id, DWORD key) return false; } -bool Pane::ProcessMouse_System(UINT event, DWORD state, DWORD x, DWORD y, const char *kstate) +bool Pane::ProcessMouse_System(const SDL_Event &event, DWORD x, DWORD y, + const char *kstate) { if (g_camera->IsExternal()) return false; // respond to mouse events only in cockpit mode bool consumed = false; - if (defpanel) /*consumed = defpanel->ProcessMouse(event, state, x, y)*/; - else if (panel2d) consumed = panel2d->ProcessMouse_System(event, state, x, y, kstate); - else if (panel) /*consumed = panel->ProcessMouse(event, state, x, y)*/; - else if (vcockpit) /*consumed = vcockpit->ProcessMouse(event, state, x, y)*/; + if (defpanel) /*consumed = defpanel->ProcessMouse(event, state, x, y)*/ {} + else if (panel2d) {consumed = panel2d->ProcessMouse_System(event, x, y, kstate);} + else if (panel) /*consumed = panel->ProcessMouse(event, state, x, y)*/ {} + else if (vcockpit) /*consumed = vcockpit->ProcessMouse(event, state, x, y)*/ {} return consumed; } -bool Pane::ProcessMouse_OnRunning (UINT event, DWORD state, DWORD x, DWORD y, const char *kstate) +bool Pane::ProcessMouse_OnRunning (const SDL_Event &event, DWORD x, DWORD y, + const char *kstate) { if (g_camera->IsExternal()) return false; // respond to mouse events only in cockpit mode bool consumed = false; - if (defpanel) consumed = defpanel->ProcessMouse (event, state, x, y); - else if (panel2d) consumed = panel2d->ProcessMouse_OnRunning (event, state, x, y, kstate); - else if (panel) consumed = panel->ProcessMouse (event, state, x, y); - else if (vcockpit) consumed = vcockpit->ProcessMouse (event, state, x, y); + if (defpanel) consumed = defpanel->ProcessMouse (event, x, y); + else if (panel2d) consumed = panel2d->ProcessMouse_OnRunning (event, x, y, kstate); + else if (panel) consumed = panel->ProcessMouse (event, x, y); + else if (vcockpit) consumed = vcockpit->ProcessMouse (event, x, y); return consumed; } diff --git a/Src/Orbiter/Pane.h b/Src/Orbiter/Pane.h index 6bc1ded42..272ed6b55 100644 --- a/Src/Orbiter/Pane.h +++ b/Src/Orbiter/Pane.h @@ -55,7 +55,7 @@ class Pane { friend class MenuInfoBar; public: - Pane (oapi::GraphicsClient *gclient, HWND hwnd, int width, int height, int bpp); + Pane (oapi::GraphicsClient *gclient, std::shared_ptr hwnd, int width, int height, int bpp); // Create a new pane with dimension width x height x bpp ~Pane (); @@ -92,9 +92,11 @@ class Pane { bool MFDConsumeKeyBuffered (int id, DWORD key); // Process a buffered key for MFD id - bool ProcessMouse_System(UINT event, DWORD state, DWORD x, DWORD y, const char *kstate); + bool ProcessMouse_System(const SDL_Event &event, DWORD x, DWORD y, + const char *kstate); - bool ProcessMouse_OnRunning (UINT event, DWORD state, DWORD x, DWORD y, const char *kstate); + bool ProcessMouse_OnRunning (const SDL_Event &event, DWORD x, DWORD y, + const char *kstate); // Process a mouse click/release void SetFOV (double _fov); @@ -271,7 +273,7 @@ class Pane { oapi::GraphicsClient *gc; // client instance int W, H, BPP; // pane dimensions int scaleW; - HWND hWnd; // window handle + std::shared_ptr hWnd; // window handle int colidx; // HUD colour index COLORREF hudCol; // HUD colour double hudIntens; // HUD intensity (VC only) diff --git a/Src/Orbiter/Panel.cpp b/Src/Orbiter/Panel.cpp index 710327837..c05535d6d 100644 --- a/Src/Orbiter/Panel.cpp +++ b/Src/Orbiter/Panel.cpp @@ -33,7 +33,7 @@ Panel::Panel (int _id, const Pane *_pane, double _scale) if (g_pOrbiter->IsFullscreen()) cwnd = 0; else - cwnd = g_pOrbiter->GetRenderWnd(); + cwnd = g_pOrbiter->GetRenderWnd()->Win32Handle(); narea = nareabuf = 0; idx_mfocus = aid_mfocus = mstate = 0; @@ -373,23 +373,21 @@ void Panel::Area2Screen (const RECT &srcR, RECT &tgtR) const Point2Screen (srcR.right, srcR.bottom, tgtR.right, tgtR.bottom); } -bool Panel::ProcessMouse (UINT event, DWORD state, int x, int y) +bool Panel::ProcessMouse (const SDL_Event &event, int x, int y) { mstate = 0; - switch (event) { - case WM_LBUTTONDOWN: - state = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; - break; - case WM_RBUTTONDOWN: - state = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; - break; - case WM_LBUTTONUP: - state = PANEL_MOUSE_LBUP; - break; - case WM_RBUTTONUP: - state = PANEL_MOUSE_RBUP; - break; - } + auto state = 0; + + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { + state = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_RIGHT) { + state = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) { + state = PANEL_MOUSE_LBUP; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_RIGHT) { + state = PANEL_MOUSE_RBUP; + } + if (state & PANEL_MOUSE_DOWN) { // locate mouse event int i; x -= X0, y -= Y0; diff --git a/Src/Orbiter/Panel.h b/Src/Orbiter/Panel.h index eec5ab0ec..4bbe7bc5f 100644 --- a/Src/Orbiter/Panel.h +++ b/Src/Orbiter/Panel.h @@ -79,7 +79,7 @@ class Panel { inline void MFDSize (int id, int &w, int &h) const { w = mfd[id].w, h = mfd[id].h; } - bool ProcessMouse (UINT event, DWORD state, int x, int y); + bool ProcessMouse (const SDL_Event &event, int x, int y); void GetMouseState (int &idx, int &state, int &mx, int &my) const; //{ idx = idx_mfocus; state = mstate; mx = mousex, my = mousey; } diff --git a/Src/Orbiter/Panel2D.cpp b/Src/Orbiter/Panel2D.cpp index 6def184ff..c4e7f2b8f 100644 --- a/Src/Orbiter/Panel2D.cpp +++ b/Src/Orbiter/Panel2D.cpp @@ -41,7 +41,7 @@ Panel2D::Panel2D (int _id, Pane *_pane, double scale) mstate = 0; if (g_pOrbiter->IsFullscreen()) cwnd = 0; - else cwnd = g_pOrbiter->GetRenderWnd(); + else cwnd = g_pOrbiter->GetRenderWnd()->Win32Handle(); for (i = 0; i < 4; i++) connect[i] = -1; @@ -281,49 +281,36 @@ void Panel2D::Render () } } -bool Panel2D::ProcessMouse_System(UINT event, DWORD state, int x, int y, const char *kstate) +bool Panel2D::ProcessMouse_System(const SDL_Event &event, int x, int y, + const char *kstate) { // Windows event handler for mouse events - switch (event) { - case WM_MOUSEWHEEL: - if ((KEYMOD_CONTROL(kstate))) { - short zDelta = (short)HIWORD(state); + if (event.type == SDL_EVENT_MOUSE_WHEEL) { + if ((KEYMOD_CONTROL(kstate))) { + double zDelta = event.wheel.y * 120.0; zoomaction = (zDelta < 0 ? ZOOM_OUT : ZOOM_IN); refx = x, refy = y; return true; } - break; } return false; } -bool Panel2D::ProcessMouse_OnRunning (UINT event, DWORD state, int x, int y, const char *kstate) +bool Panel2D::ProcessMouse_OnRunning (const SDL_Event &event, int x, int y, + const char *kstate) { mstate = 0; - - // Windows event handler for mouse events - switch (event) { - case WM_LBUTTONDOWN: - state = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; - break; - case WM_RBUTTONDOWN: - state = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; - break; - case WM_LBUTTONUP: - state = PANEL_MOUSE_LBUP; - break; - case WM_RBUTTONUP: - state = PANEL_MOUSE_RBUP; - break; - //case WM_MOUSEWHEEL: - // if ((KEYMOD_CONTROL(kstate))) { - // short zDelta = (short)HIWORD(state); - // zoomaction = (zDelta < 0 ? ZOOM_OUT : ZOOM_IN); - // refx = x, refy = y; - // return true; - // } - // break; - } + auto state = 0; + + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { + state = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_RIGHT) { + state = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) { + state = PANEL_MOUSE_LBUP; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_RIGHT) { + state = PANEL_MOUSE_RBUP; + } // mouse state event handler (button-down events only) if (state & PANEL_MOUSE_DOWN) { diff --git a/Src/Orbiter/Panel2D.h b/Src/Orbiter/Panel2D.h index 33e8a9556..1b7b47700 100644 --- a/Src/Orbiter/Panel2D.h +++ b/Src/Orbiter/Panel2D.h @@ -123,22 +123,22 @@ class Panel2D { /** * \brief Process a mouse event for the panel. * \param event event type (see \ref panel_mouse) - * \param state mouse button state * \param x mouse screen x position * \param y mouse screen y position * \return \e true if the panel processes the event. */ - bool ProcessMouse_System(UINT event, DWORD state, int x, int y, const char *kstate); + bool ProcessMouse_System(const SDL_Event &event, int x, int y, + const char *kstate); /** * \brief Process a mouse event for the panel while the simulation is active. * \param event event type (see \ref panel_mouse) - * \param state mouse button state * \param x mouse screen x position * \param y mouse screen y position * \return \e true if the panel processes the event. */ - bool ProcessMouse_OnRunning (UINT event, DWORD state, int x, int y, const char *kstate); + bool ProcessMouse_OnRunning (const SDL_Event &event, int x, int y, + const char *kstate); void GetMouseState (int &idx, int &state, int &mx, int &my) const; diff --git a/Src/Orbiter/PlaybackEd.cpp b/Src/Orbiter/PlaybackEd.cpp index 6290d6a95..4186059d6 100644 --- a/Src/Orbiter/PlaybackEd.cpp +++ b/Src/Orbiter/PlaybackEd.cpp @@ -2,23 +2,233 @@ // Licensed under the MIT License #include "PlaybackEd.h" -#include "DlgMgr.h" // remove #include "Camera.h" #include "Log.h" -#include "Resource.h" +#include "imgui_extras.h" using namespace std; extern Orbiter *g_pOrbiter; extern Camera *g_camera; extern TimeData td; -extern char DBG_MSG[256]; -INT_PTR CALLBACK RecPlayAnn_DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +static bool CompareEvents (const PlaybackEvent *first, const PlaybackEvent *second) +{ + return first->T0() < second->T0(); +} + +DlgPlaybackEditor::DlgPlaybackEditor() : ImGuiDialog("Playback Editor", {900,600}) { + SetHelp("html/orbiter.chm", "/playbackedit.htm"); + + m_sysfname = nullptr; + m_SelectedEvent = nullptr; +} + +DlgPlaybackEditor::~DlgPlaybackEditor() { + ClearEvents(); +} + +void DlgPlaybackEditor::OnDraw() { + ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; + + ImGui::Text("Playback Editor - Simulation time : %f", td.SimT0); + ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x/2.0, 0), ImGuiChildFlags_ResizeX, window_flags); + + const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchProp|ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; + + ImVec2 outer_size = ImVec2(0.0f, ImGui::GetContentRegionAvail().y - 100); + if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size)) + { + ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible + ImGui::TableSetupColumn("Timestamp", ImGuiTableColumnFlags_None, 0.5f); + ImGui::TableSetupColumn("Event", ImGuiTableColumnFlags_None, 0.5f); + ImGui::TableSetupColumn("Details", ImGuiTableColumnFlags_None, 2.0f); + ImGui::TableHeadersRow(); + + int i = 0; + bool ts_drawn = false; + if(m_Events.size() == 0) { + ImGui::TableNextRow(); + ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImColor(1.0,0,0)); + ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, ImColor(1.0,0,0)); + ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImColor(1.0,0,0)); + ImGui::TableSetColumnIndex(0); + ImGui::Text("%0.2f", td.SimT0); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("NOW"); + } + for(auto &e : m_Events) { + i++; + const bool item_is_selected = e == m_SelectedEvent; + + if(e->T0() > td.SimT0 && !ts_drawn) { + ts_drawn = true; + ImGui::TableNextRow(); + ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImColor(1.0,0,0)); + ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, ImColor(1.0,0,0)); + ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImColor(1.0,0,0)); + ImGui::TableSetColumnIndex(0); + ImGui::Text("%0.2f", td.SimT0); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("NOW"); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + char label[32]; + sprintf(label, "%0.2f###line%d", e->T0(),i); + ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SpanAllColumns; + if (ImGui::Selectable(label, item_is_selected, selectable_flags)) + { + m_SelectedEvent = e; + } + + char tag[32]; + e->TagStr(tag); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s",tag); + ImGui::TableSetColumnIndex(2); + e->DrawPreview(); + } + + ImGui::EndTable(); + } + + ImGui::BeginGroupPanel("Insert event"); + if(ImGui::Button("NOTE")) { + InsertEvent(new NoteEvent (td.SimT0, "New note")); + } + ImGui::SameLine(); + if(ImGui::Button("NOTEOFF")) { + InsertEvent(new NoteoffEvent (td.SimT0)); + } + ImGui::SameLine(); + if(ImGui::Button("NOTEPOS")) { + InsertEvent(new NoteposEvent (td.SimT0, 0.1,0.1,0.9,0.9)); + } + ImGui::SameLine(); + if(ImGui::Button("NOTECOL")) { + InsertEvent(new NotecolEvent (td.SimT0, 1.0, 1.0, 1.0)); + } + ImGui::SameLine(); + if(ImGui::Button("NOTESIZE")) { + InsertEvent(new NotesizeEvent (td.SimT0, 1.0)); + } + if(ImGui::Button("TACC")) { + InsertEvent(new TaccEvent (td.SimT0, 1.0, 0.0)); + } + ImGui::SameLine(); + if(ImGui::Button("CAMERA")) { + InsertEvent(new CameraEvent (td.SimT0)); + } + ImGui::SameLine(); + if(ImGui::Button("")) { + InsertEvent(new GenericEvent (td.SimT0, "", "")); + } + ImGui::EndGroupPanel(); + if(ImGui::Button("Delete")) { + DeleteSelectedEvent(); + } + ImGui::EndChild(); + ImGui::SameLine(); +// ImGui::BeginChild("ChildR", ImVec2(sz2, ImGui::GetContentRegionAvail().y), false, window_flags); + ImGui::BeginChild("ChildR", ImVec2(0,0), 0, window_flags); + ImGui::BeginChild("ChildRT", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - 40), true, window_flags); + if(m_SelectedEvent) { + m_SelectedEvent->DrawEdit(); + if(ImGui::Button("Apply")) { + m_SelectedEvent->ApplyChanges(); + m_Events.sort(CompareEvents); + } + + } + ImGui::EndChild(); + ImGui::BeginChild("ChildRB", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), true, window_flags); + if(ImGui::Button("Commit changes")) { + SaveEventFile(); + } + ImGui::SameLine(); + if(ImGui::Button("Discard uncommited changes")) { + ClearEvents(); + ScanEventFile(); + } + + ImGui::EndChild(); + ImGui::EndChild(); +} + +void DlgPlaybackEditor::InsertEvent(PlaybackEvent *e) { + m_Events.push_back(e); + m_Events.sort(CompareEvents); + m_SelectedEvent = e; +} + +void DlgPlaybackEditor::DeleteSelectedEvent() { + if(m_SelectedEvent==nullptr) return; + PlaybackEvent *prev = nullptr; + for(auto it = m_Events.begin(); it!=m_Events.end();++it) { + if(*it==m_SelectedEvent) { + m_Events.erase(it); + if(prev) { + m_SelectedEvent = prev; + } else if(m_Events.size()>0) { + m_SelectedEvent = m_Events.front(); + } else { + m_SelectedEvent = nullptr; + } + return; + } + prev = *it; + } +} + +void DlgPlaybackEditor::Load(const char *ScnName) { + char fname[1024]; + int i; + for (i = strlen(ScnName)-1; i > 0; i--) + if (ScnName[i-1] == '/' || ScnName[i-1] == '\\') break; + sprintf (fname, "Flights/%s/system.dat", ScnName+i); + + if(m_sysfname) free(m_sysfname); + m_sysfname = strdup(fname); + ClearEvents(); + ScanEventFile(); +} + +void DlgPlaybackEditor::ScanEventFile () +{ + char line[2048]; + ifstream ifs (m_sysfname); + while (ifs.getline (line, 2048)) { + PlaybackEvent *pe = PlaybackEvent::Create (line); + if (pe) { + m_Events.push_back(pe); + } + } +} + +void DlgPlaybackEditor::SaveEventFile () +{ + g_pOrbiter->FRecorder_SuspendPlayback(); + ofstream ofs (m_sysfname); + for (auto &e: m_Events) { + e->Write (ofs); + } + ofs.close(); + g_pOrbiter->FRecorder_RescanPlayback(); +} + +void DlgPlaybackEditor::ClearEvents() { + for(auto &e : m_Events) { + delete e; + } + m_Events.clear(); + m_SelectedEvent = nullptr; +} // ========================================================= -PlaybackEvent *PlaybackEvent::Create (PlaybackEditor *editor, char *event) +PlaybackEvent *PlaybackEvent::Create (char *event) { char *s; double t; @@ -33,45 +243,47 @@ PlaybackEvent *PlaybackEvent::Create (PlaybackEditor *editor, char *event) s = strtok (NULL, " \t\n"); if (!s || sscanf (s, "%lf", &delay) != 1) delay = 0.0; - TRACENEW; return new TaccEvent (editor, t, acc, delay); + TRACENEW; return new TaccEvent (t, acc, delay); } else if (!_stricmp (s, "CAMERA")) { - DWORD pr; + int32_t pr; s = strtok (NULL, " \t\n"); if (!s) return NULL; if (!_stricmp (s, "PRESET")) { s = strtok (NULL, " \t\n"); if (!s || sscanf (s, "%d", &pr) != 1) return NULL; - TRACENEW; return new CameraEvent (editor, t, pr); + TRACENEW; return new CameraEvent (t, pr); } else if (!strcmp (s, "SET")) { - TRACENEW; return new CameraEvent (editor, t, s+4); + TRACENEW; return new CameraEvent (t, s+4); } } else if (!_stricmp (s, "NOTE")) { - TRACENEW; return new NoteEvent (editor, t, strtok (NULL, "\n")); + TRACENEW; return new NoteEvent (t, strtok (NULL, "\n")); + } else if (!_stricmp (s, "NOTEOFF")) { + TRACENEW; return new NoteoffEvent (t); } else if (!_stricmp (s, "NOTEPOS")) { double x0, y0, x1, y1; int res = sscanf (s+8, "%lf %lf %lf %lf", &x0, &y0, &x1, &y1); if (res != 4) return NULL; - else { TRACENEW; return new NoteposEvent (editor, t, x0, y0, x1, y1); } + else { TRACENEW; return new NoteposEvent (t, x0, y0, x1, y1); } } else if (!_stricmp (s, "NOTECOL")) { double r, g, b; int res = sscanf (s+8, "%lf %lf %lf", &r, &g, &b); if (res != 3) return NULL; - else { TRACENEW; return new NotecolEvent (editor, t, r, g, b); } + else { TRACENEW; return new NotecolEvent (t, r, g, b); } } else if (!_stricmp (s, "NOTESIZE")) { double scale; int res = sscanf (s+9, "%lf", &scale); if (!res) return NULL; - else { TRACENEW; return new NotesizeEvent (editor, t, scale); } + else { TRACENEW; return new NotesizeEvent (t, scale); } } else { - TRACENEW; return new GenericEvent (editor, t, s, strtok (NULL, "\n")); + TRACENEW; return new GenericEvent (t, s, strtok (NULL, "\n")); } return NULL; } -PlaybackEvent::PlaybackEvent (PlaybackEditor *_editor, double _t0) +PlaybackEvent::PlaybackEvent (double _t0) { - editor = _editor; t0 = _t0; + sprintf(m_tmp_t0, "%0.2f", t0); } void PlaybackEvent::TimeStr (char *str) @@ -86,58 +298,16 @@ void PlaybackEvent::WriteEvent (ofstream &ofs, const char *eventtype, const char ofs << endl; } -void PlaybackEvent::EditEvent (PlaybackEditor *editor) -{ - HWND hEditor = editor->EditTab(); - if (hEditor) { - char cbuf[64]; - sprintf (cbuf, "%0.3f", t0); - SetWindowText (GetDlgItem (hEditor, IDC_EVTIME), cbuf); - } +void PlaybackEvent::DrawEdit() { + ImGui::InputText("Event Time", m_tmp_t0, sizeof(m_tmp_t0), ImGuiInputTextFlags_CharsDecimal); } -void PlaybackEvent::CommitEdit () -{ - HWND hEdit = editor->EditTab(); - if (hEdit) { - char cbuf[256]; - double t; - GetWindowText (GetDlgItem (hEdit, IDC_EVTIME), cbuf, 256); - if (sscanf (cbuf, "%lf", &t) == 1 && fabs (t-t0) > 1e-3) { - t0 = t; - // check if we need to re-sort the event list - editor->SortEvent (this); - } - } +void PlaybackEvent::ApplyChanges() { + t0 = atof(m_tmp_t0); } - -INT_PTR PlaybackEvent::MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_NOTIFY: - if (((NMHDR*)lParam)->idFrom == IDC_SPIN1) { - if (((NMHDR*)lParam)->code == UDN_DELTAPOS) { - NMUPDOWN *nmud = (NMUPDOWN*)lParam; - char cbuf[256]; - double et; - GetWindowText (GetDlgItem (hDlg, IDC_EVTIME), cbuf, 256); - if (sscanf (cbuf, "%lf", &et)) { - et -= nmud->iDelta; - et = max (et, 0.0); - sprintf (cbuf, "%f", et); - SetWindowText (GetDlgItem (hDlg, IDC_EVTIME), cbuf); - CommitEdit(); - } - } - } - break; - } - return FALSE; -} - // ========================================================= -GenericEvent::GenericEvent (PlaybackEditor *_editor, double _t0, const char *_tag, const char *_content): PlaybackEvent (_editor, _t0) +GenericEvent::GenericEvent (double _t0, const char *_tag, const char *_content): PlaybackEvent (_t0) { content = tag = 0; SetTag (_tag); @@ -146,22 +316,13 @@ GenericEvent::GenericEvent (PlaybackEditor *_editor, double _t0, const char *_ta GenericEvent::~GenericEvent () { - if (tag) { - delete []tag; - tag = NULL; - } - if (content) { - delete []content; - content = NULL; - } + if (tag) delete []tag; + if (content) delete []content; } void GenericEvent::SetTag (const char *_tag) { - if (tag) { - delete []tag; - tag = NULL; - } + if (tag) delete []tag; if (_tag) { tag = new char[strlen(_tag)+1]; TRACENEW strcpy (tag, _tag); @@ -170,10 +331,7 @@ void GenericEvent::SetTag (const char *_tag) void GenericEvent::SetContent (const char *_content) { - if (content) { - delete []content; - content = NULL; - } + if (content) delete []content; if (_content) { content = new char[strlen(_content)+1]; TRACENEW strcpy (content, _content); @@ -196,58 +354,36 @@ void GenericEvent::Write (ofstream &ofs) WriteEvent (ofs, tag, content); } -void GenericEvent::EditEvent (PlaybackEditor *editor) -{ - editor->OpenEditTab (this, IDD_PBEDITOR_GENERIC, EditProc); - PlaybackEvent::EditEvent (editor); - if (tag) - SetWindowText (GetDlgItem (editor->EditTab(), IDC_EDIT3), tag); +void GenericEvent::DrawPreview() { if (content) - SetWindowText (GetDlgItem (editor->EditTab(), IDC_EDIT1), content); -} - -void GenericEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - char cbuf[2048]; - HWND hEdit = editor->EditTab(); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT3), cbuf, 2048); - SetTag (cbuf); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf, 2048); - SetContent (cbuf); -} - -INT_PTR GenericEvent::MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - return PlaybackEvent::MsgProc (hDlg, uMsg, wParam, lParam); + ImGui::TextUnformatted(content); + else + ImGui::TextUnformatted("---"); } -INT_PTR CALLBACK GenericEvent::EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - GenericEvent *event = (uMsg == WM_INITDIALOG ? - (GenericEvent*)lParam : (GenericEvent*)GetWindowLongPtr (hDlg, GWLP_USERDATA)); - return (event ? event->MsgProc (hDlg, uMsg, wParam, lParam) : FALSE); +void GenericEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + char ltag[4096]; + char lcontent[4096]; + if(tag) strncpy(ltag, tag, 4095); + if(content) strncpy(lcontent, content, 4095); + ltag[4095]='\0'; + lcontent[4095]='\0'; + if(ImGui::InputText("Tag", ltag, sizeof(ltag))) { + SetTag(ltag); + } + if(ImGui::InputText("Content", lcontent, sizeof(lcontent))) { + SetContent(lcontent); + } } // ========================================================= -TaccEvent::TaccEvent (PlaybackEditor *_editor, double _t0, double _tacc, double _delay): PlaybackEvent (_editor, _t0) +TaccEvent::TaccEvent (double _t0, double _tacc, float _delay): PlaybackEvent (_t0) { - tacc = _tacc; + tmp_tacc = tacc = _tacc; delay = _delay; -} - -void TaccEvent::SetTacc (double _tacc) -{ - tacc = min (1e5, max (0.1, _tacc)); - char cbuf[256]; - sprintf (cbuf, "%0.2f", tacc); - SetWindowText (GetDlgItem (editor->EditTab(), IDC_EDIT1), cbuf); -} - -void TaccEvent::SetDelay (double _delay) -{ - delay = max (0.0, _delay); + sprintf(tmp_delay, "%f", delay); } void TaccEvent::TagStr (char *str) @@ -272,72 +408,31 @@ void TaccEvent::Write (ofstream &ofs) WriteEvent (ofs, "TACC", cbuf); } -void TaccEvent::EditEvent (PlaybackEditor *editor) -{ - editor->OpenEditTab (this, IDD_PBEDITOR_TACC, EditProc); - PlaybackEvent::EditEvent (editor); - char cbuf[128]; - sprintf (cbuf, "%0.2f", tacc); - SetWindowText (GetDlgItem (editor->EditTab(), IDC_EDIT1), cbuf); - sprintf (cbuf, "%0.2f", delay); - SetWindowText (GetDlgItem (editor->EditTab(), IDC_EDIT2), cbuf); -} - -void TaccEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - char cbuf[2048]; - double ta, dl; - HWND hEdit = editor->EditTab(); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf, 2048); - sscanf (cbuf, "%lf", &ta); - SetTacc (ta); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT2), cbuf, 2048); - sscanf (cbuf, "%lf", &dl); - SetDelay (dl); -} - -INT_PTR TaccEvent::MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_BUTTON1: - SetTacc (0.1); - return FALSE; - case IDC_BUTTON2: - SetTacc (1.0); - return FALSE; - case IDC_BUTTON3: - SetTacc (10.0); - return FALSE; - case IDC_BUTTON4: - SetTacc (1e2); - return FALSE; - case IDC_BUTTON5: - SetTacc (1e3); - return FALSE; - case IDC_BUTTON6: - SetTacc (1e4); - return FALSE; - } - break; +void TaccEvent::DrawPreview() { + if (delay) { + ImGui::Text ("%0.2fx (delay %0.1f)", tacc, delay); + } else { + ImGui::Text ("%0.2fx", tacc); } - return PlaybackEvent::MsgProc (hDlg, uMsg, wParam, lParam); } -INT_PTR CALLBACK TaccEvent::EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - TaccEvent *event = (uMsg == WM_INITDIALOG ? - (TaccEvent*)lParam : (TaccEvent*)GetWindowLongPtr (hDlg, GWLP_USERDATA)); - return (event ? event->MsgProc (hDlg, uMsg, wParam, lParam) : FALSE); -} +void TaccEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + + ImGui::SliderFloat("Time Acceleration", &tmp_tacc, 0.1f, 10000.0f, "%.1f", ImGuiSliderFlags_Logarithmic); + ImGui::InputText("Delay (s)", tmp_delay, sizeof(tmp_delay), ImGuiInputTextFlags_CharsDecimal); +} +void TaccEvent::ApplyChanges() { + PlaybackEvent::ApplyChanges(); + tacc = tmp_tacc; + delay = atof(tmp_delay); +} // ========================================================= -CameraEvent::CameraEvent (PlaybackEditor *_editor, double _t0, DWORD _preset): PlaybackEvent (_editor, _t0) +CameraEvent::CameraEvent (double _t0, int _preset): PlaybackEvent (_t0) { - if (_preset != (DWORD)-1) { + if (_preset != -1) { SetPreset (_preset); } else { char cbuf[256]; @@ -347,22 +442,27 @@ CameraEvent::CameraEvent (PlaybackEditor *_editor, double _t0, DWORD _preset): P } } -CameraEvent::CameraEvent (PlaybackEditor *_editor, double _t0, char *_modestr): PlaybackEvent (_editor, _t0) +CameraEvent::CameraEvent (double _t0, char *_modestr): PlaybackEvent (_t0) { SetInlineMode (_modestr); } -void CameraEvent::SetPreset (DWORD _preset) +void CameraEvent::SetPreset (int _preset, bool editmode) { - preset = _preset; - sprintf (modestr, "PRESET %d", preset); + if(!editmode) { + sprintf (modestr, "PRESET %d", _preset); + } + sprintf (m_tmp_modestr, "PRESET %d", _preset); } -void CameraEvent::SetInlineMode (char *mode) +void CameraEvent::SetInlineMode (char *mode, bool editmode) { - preset = (DWORD)-1; - strcpy (modestr, "SET "); - strcat (modestr, mode); + if(!editmode) { + strcpy (modestr, "SET "); + strcat (modestr, mode); + } + strcpy (m_tmp_modestr, "SET "); + strcat (m_tmp_modestr, mode); } void CameraEvent::TagStr (char *str) @@ -380,111 +480,58 @@ void CameraEvent::Write (ofstream &ofs) WriteEvent (ofs, "CAMERA", modestr); } -void CameraEvent::EditEvent (PlaybackEditor *editor) -{ - editor->OpenEditTab (this, IDD_PBEDITOR_CAMPRESET, EditProc); - PlaybackEvent::EditEvent (editor); - if (preset != (DWORD)-1) - SendDlgItemMessage (editor->EditTab(), IDC_COMBO1, CB_SETCURSEL, preset, 0); -} - -void CameraEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - HWND hEdit = editor->EditTab(); - DWORD idx = SendDlgItemMessage (hEdit, IDC_COMBO1, CB_GETCURSEL, 0, 0); - if (idx != CB_ERR) SetPreset (idx); -} - -void CameraEvent::ScanPresets (HWND hTab) -{ - // populate the list of available presets - DWORD i, np = g_camera->nPreset(); - char cbuf[256], descr[256]; - SendDlgItemMessage (hTab, IDC_COMBO1, CB_RESETCONTENT, 0, 0); - for (i = 0; i < np; i++) { - CameraMode *cm = g_camera->GetPreset (i); - cm->GetDescr (descr, 256); - sprintf (cbuf, "%d (%s)", i, descr); - SendDlgItemMessage (hTab, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)cbuf); +void CameraEvent::DrawPreview() { + ImGui::TextUnformatted (modestr); +} + +void CameraEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + ImGui::Text("Camera configuration: "); + ImGui::SameLine(); + ImGui::TextUnformatted(m_tmp_modestr); + + char descr[256]; + int np = g_camera->nPreset(); + if(np) { + ImGui::Separator(); + ImGui::TextUnformatted("Presets:"); + for(int i=0;iGetPreset (i); + cm->GetDescr (descr, 256); + char cbuf[280]; + sprintf(cbuf, "%d (%s)", i, descr); + if(ImGui::Button(cbuf)) { + SetPreset(i, true); + } + } } - if (preset < np) - SendDlgItemMessage (hTab, IDC_COMBO1, CB_SETCURSEL, preset, 0); - else - SetWindowText (GetDlgItem (hTab, IDC_EDIT1), modestr+4); -} - -void CameraEvent::AddCurrentView (HWND hTab) -{ - char cbuf[256]; + ImGui::Separator(); + ImGui::Text("Current view: "); CameraMode *cm = g_camera->GetCMode(); + char cbuf[256]; cm->Store (cbuf); - SetInlineMode (cbuf); - SendDlgItemMessage (hTab, IDC_COMBO1, CB_SETCURSEL, -1, 0); - SetWindowText (GetDlgItem (hTab, IDC_EDIT1), cbuf); -} - -INT_PTR CameraEvent::MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_INITDIALOG: - ScanPresets (hDlg); - return FALSE; - case WM_COMMAND: - switch (LOWORD (wParam)) { - case IDC_COMBO1: - if (HIWORD (wParam) == CBN_SELCHANGE) { - DWORD idx = SendDlgItemMessage (hDlg, IDC_COMBO1, CB_GETCURSEL, 0, 0); - SetPreset (idx); - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), ""); - return FALSE; - } - break; - case IDC_BUTTON1: - AddCurrentView (hDlg); - return FALSE; - } - break; + ImGui::SameLine(); + if(ImGui::Button(cbuf)) { + SetInlineMode (cbuf, true); } - return PlaybackEvent::MsgProc (hDlg, uMsg, wParam, lParam); } +void CameraEvent::ApplyChanges() { + PlaybackEvent::ApplyChanges(); - -INT_PTR CALLBACK CameraEvent::EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - CameraEvent *event = (uMsg == WM_INITDIALOG ? - (CameraEvent*)lParam : (CameraEvent*)GetWindowLongPtr (hDlg, GWLP_USERDATA)); - return (event ? event->MsgProc (hDlg, uMsg, wParam, lParam) : FALSE); + strcpy(modestr, m_tmp_modestr); } // ========================================================= -NoteEvent::NoteEvent (PlaybackEditor *_editor, double _t0, const char *_note): PlaybackEvent (_editor, _t0) +NoteEvent::NoteEvent (double _t0, const char *_note): PlaybackEvent (_t0) { - note = NULL; - SetNote (_note); + note = strdup(_note); + strncpy(m_tmp_note, note, sizeof(m_tmp_note)); } NoteEvent::~NoteEvent () { - if (note) { - delete []note; - note = NULL; - } -} - -void NoteEvent::SetNote (const char *_note) -{ - if (note) { - delete []note; - note = NULL; - } - if (_note) { - note = new char[strlen(_note)+1]; TRACENEW - strcpy (note, _note); - } else { - note = 0; - } + if (note) free(note); } void NoteEvent::TagStr (char *str) @@ -503,37 +550,28 @@ void NoteEvent::Write (ofstream &ofs) WriteEvent (ofs, "NOTE", note ? note : ""); } -void NoteEvent::EditEvent (PlaybackEditor *editor) -{ - editor->OpenEditTab (this, IDD_PBEDITOR_NOTE, EditProc); - PlaybackEvent::EditEvent (editor); - SetWindowText (GetDlgItem (editor->EditTab(), IDC_EDIT1), note); +void NoteEvent::DrawPreview() { + if (note) { + ImGui::TextUnformatted (note); + } else { + ImGui::TextUnformatted ("---"); + } } -void NoteEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - char cbuf[2048]; - HWND hEdit = editor->EditTab(); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf, 2048); - SetNote (cbuf); +void NoteEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; + ImGui::InputTextMultiline("##NoteEvent", m_tmp_note, IM_ARRAYSIZE(m_tmp_note), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - 30), flags); } - -INT_PTR CALLBACK NoteEvent::EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - NoteEvent *event = (uMsg == WM_INITDIALOG ? - (NoteEvent*)lParam : (NoteEvent*)GetWindowLongPtr (hDlg, GWLP_USERDATA)); - return (event ? event->MsgProc (hDlg, uMsg, wParam, lParam) : FALSE); +void NoteEvent::ApplyChanges() { + PlaybackEvent::ApplyChanges(); + if(note) free(note); + note = strdup(m_tmp_note); } // ========================================================= -NoteposEvent::NoteposEvent (PlaybackEditor *_editor, double _t0, double _x0, double _y0, double _x1, double _y1): PlaybackEvent (_editor, _t0) -{ - SetPos (_x0, _y0, _x1, _y1); -} - -void NoteposEvent::SetPos (double _x0, double _y0, double _x1, double _y1) +NoteposEvent::NoteposEvent (double _t0, double _x0, double _y0, double _x1, double _y1): PlaybackEvent (_t0) { x0 = _x0; y0 = _y0; @@ -558,50 +596,93 @@ void NoteposEvent::Write (ofstream &ofs) WriteEvent (ofs, "NOTEPOS", cbuf); } -void NoteposEvent::EditEvent (PlaybackEditor *editor) -{ - char cbuf[256]; - editor->OpenEditTab (this, IDD_PBEDITOR_NOTEPOS, EditProc); - PlaybackEvent::EditEvent (editor); - HWND hEdit = editor->EditTab(); - sprintf (cbuf, "%0.2f", x0); SetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf); - sprintf (cbuf, "%0.2f", y0); SetWindowText (GetDlgItem (hEdit, IDC_EDIT2), cbuf); - sprintf (cbuf, "%0.2f", x1); SetWindowText (GetDlgItem (hEdit, IDC_EDIT3), cbuf); - sprintf (cbuf, "%0.2f", y1); SetWindowText (GetDlgItem (hEdit, IDC_EDIT4), cbuf); +void NoteposEvent::DrawPreview() { + ImGui::Text ("%g %g %g %g", x0, y0, x1, y1); } -void NoteposEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - char cbuf[256]; - HWND hEdit = editor->EditTab(); - double _x0, _y0, _x1, _y1; - GetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf, 256); sscanf (cbuf, "%lf", &_x0); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT2), cbuf, 256); sscanf (cbuf, "%lf", &_y0); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT3), cbuf, 256); sscanf (cbuf, "%lf", &_x1); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT4), cbuf, 256); sscanf (cbuf, "%lf", &_y1); - SetPos (_x0, _y0, _x1, _y1); +void NoteposEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + + ImVec2 virtual_screen_size(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x * 3.0f/4.0f); + ImGui::InvisibleButton("##empty", virtual_screen_size); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const ImVec2 p0 = ImGui::GetItemRectMin(); + const ImVec2 p1 = ImGui::GetItemRectMax(); + draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)); + + ImVec2 wpos1 = ImVec2(m_offsetPos.x+p0.x+x0 * virtual_screen_size.x,m_offsetPos.y+ p0.y+y0 * virtual_screen_size.y); + ImVec2 wpos2 = ImVec2(m_offsetPos.x+p0.x+m_offsetSize.x+x1 * virtual_screen_size.x,m_offsetPos.y+ p0.y+m_offsetSize.y+y1 * virtual_screen_size.y); + + ImGui::PushClipRect(p0, p1, true); + draw_list->AddRectFilled(wpos1, wpos2, IM_COL32(70, 70, 100, 255)); + ImVec2 tri1 = ImVec2(wpos2.x - 10, wpos2.y); + ImVec2 tri2 = ImVec2(wpos2.x, wpos2.y - 10); + draw_list->AddRect(wpos1, wpos2, IM_COL32(20, 20, 40, 255)); + draw_list->AddTriangleFilled(wpos2, tri1, tri2, IM_COL32(20, 20, 40, 255)); + ImGui::PopClipRect(); + + float mx = ImGui::GetIO().MousePos.x; + float my = ImGui::GetIO().MousePos.y; + + if(wpos1.xMsgProc (hDlg, uMsg, wParam, lParam) : FALSE); + m_offsetPos = {0,0}; + m_offsetSize = {0,0}; + m_state = 0; } // ========================================================= -NotecolEvent::NotecolEvent (PlaybackEditor *_editor, double _t0, double _r, double _g, double _b): PlaybackEvent (_editor, _t0) -{ - SetCol (_r, _g, _b); -} - -void NotecolEvent::SetCol (double _r, double _g, double _b) +NotecolEvent::NotecolEvent (double _t0, double _r, double _g, double _b): PlaybackEvent (_t0) { r = _r; g = _g; b = _b; + m_tmp[0] = (float)r; + m_tmp[1] = (float)g; + m_tmp[2] = (float)b; } void NotecolEvent::TagStr (char *str) @@ -621,75 +702,29 @@ void NotecolEvent::Write (ofstream &ofs) WriteEvent (ofs, "NOTECOL", cbuf); } -void NotecolEvent::EditEvent (PlaybackEditor *editor) -{ - char cbuf[256]; - editor->OpenEditTab (this, IDD_PBEDITOR_NOTECOL, EditProc); - PlaybackEvent::EditEvent (editor); - HWND hEdit = editor->EditTab(); - sprintf (cbuf, "%g", r); SetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf); - sprintf (cbuf, "%g", g); SetWindowText (GetDlgItem (hEdit, IDC_EDIT2), cbuf); - sprintf (cbuf, "%g", b); SetWindowText (GetDlgItem (hEdit, IDC_EDIT3), cbuf); +void NotecolEvent::DrawPreview() { + ImVec4 col = { (float)r,(float)g,(float)b,1.0 }; + ImGui::TextColored(col, "COLOR"); } -void NotecolEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - char cbuf[256]; - HWND hEdit = editor->EditTab(); - double _r, _g, _b; - GetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf, 256); sscanf (cbuf, "%lf", &_r); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT2), cbuf, 256); sscanf (cbuf, "%lf", &_g); - GetWindowText (GetDlgItem (hEdit, IDC_EDIT3), cbuf, 256); sscanf (cbuf, "%lf", &_b); - SetCol (_r, _g, _b); -} - -INT_PTR NotecolEvent::MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_NOTIFY: { - int editid = 0; - switch (((NMHDR*)lParam)->idFrom) { - case IDC_SPIN2: editid = IDC_EDIT1; break; - case IDC_SPIN3: editid = IDC_EDIT2; break; - case IDC_SPIN4: editid = IDC_EDIT3; break; - } - if (editid) { - if (((NMHDR*)lParam)->code == UDN_DELTAPOS) { - NMUPDOWN *nmud = (NMUPDOWN*)lParam; - char cbuf[256]; - double col; - GetWindowText (GetDlgItem (hDlg, editid), cbuf, 256); - if (sscanf (cbuf, "%lf", &col)) { - col -= nmud->iDelta*0.1; - col = min (max (col, 0.0), 1.0); - sprintf (cbuf, "%f", col); - SetWindowText (GetDlgItem (hDlg, editid), cbuf); - } - } - } - } break; - } - return PlaybackEvent::MsgProc (hDlg, uMsg, wParam, lParam); +void NotecolEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + ImGui::Text("Note Color"); + ImGui::ColorPicker3("Color", m_tmp, ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoLabel); } - -INT_PTR CALLBACK NotecolEvent::EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - NotecolEvent *event = (uMsg == WM_INITDIALOG ? - (NotecolEvent*)lParam : (NotecolEvent*)GetWindowLongPtr (hDlg, GWLP_USERDATA)); - return (event ? event->MsgProc (hDlg, uMsg, wParam, lParam) : FALSE); +void NotecolEvent::ApplyChanges() { + PlaybackEvent::ApplyChanges(); + r = m_tmp[0]; + g = m_tmp[1]; + b = m_tmp[2]; } // ========================================================= -NotesizeEvent::NotesizeEvent (PlaybackEditor *_editor, double _t0, double _size): PlaybackEvent (_editor, _t0) -{ - SetSize (_size); -} - -void NotesizeEvent::SetSize (double _size) +NotesizeEvent::NotesizeEvent (double _t0, double _size): PlaybackEvent (_t0) { size = _size; + sprintf(m_tmp_size,"%f",size); } void NotesizeEvent::TagStr (char *str) @@ -709,442 +744,47 @@ void NotesizeEvent::Write (ofstream &ofs) WriteEvent (ofs, "NOTESIZE", cbuf); } -void NotesizeEvent::EditEvent (PlaybackEditor *editor) -{ - char cbuf[256]; - editor->OpenEditTab (this, IDD_PBEDITOR_NOTESIZE, EditProc); - PlaybackEvent::EditEvent (editor); - HWND hEdit = editor->EditTab(); - sprintf (cbuf, "%g", size); SetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf); +void NotesizeEvent::DrawPreview() { + ImGui::Text ("%g", size); } -void NotesizeEvent::CommitEdit () -{ - PlaybackEvent::CommitEdit(); - char cbuf[256]; - HWND hEdit = editor->EditTab(); - double _size; - GetWindowText (GetDlgItem (hEdit, IDC_EDIT1), cbuf, 256); sscanf (cbuf, "%lf", &_size); - SetSize (_size); +void NotesizeEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); + ImGui::InputText("Note Size", m_tmp_size, sizeof(m_tmp_size), ImGuiInputTextFlags_CharsDecimal); } - -INT_PTR NotesizeEvent::MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_NOTIFY: - if (((NMHDR*)lParam)->code == UDN_DELTAPOS) { - NMUPDOWN *nmud = (NMUPDOWN*)lParam; - char cbuf[256]; - double _size; - GetWindowText (GetDlgItem (hDlg, IDC_EDIT1), cbuf, 256); - if (sscanf (cbuf, "%lf", &_size)) { - _size -= nmud->iDelta*0.1; - _size = max (_size, 0.0); - sprintf (cbuf, "%f", _size); - SetWindowText (GetDlgItem (hDlg, IDC_EDIT1), cbuf); - } - } - break; - } - return PlaybackEvent::MsgProc (hDlg, uMsg, wParam, lParam); +void NotesizeEvent::ApplyChanges() { + PlaybackEvent::ApplyChanges(); + size = atof(m_tmp_size); } -INT_PTR CALLBACK NotesizeEvent::EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - NotesizeEvent *event = (uMsg == WM_INITDIALOG ? - (NotesizeEvent*)lParam : (NotesizeEvent*)GetWindowLongPtr (hDlg, GWLP_USERDATA)); - return (event ? event->MsgProc (hDlg, uMsg, wParam, lParam) : FALSE); -} - -// ========================================================= // ========================================================= -PlaybackEditor::PlaybackEditor (Orbiter *ob, const char *ScnName) +NoteoffEvent::NoteoffEvent (double _t0): PlaybackEvent (_t0) { - int i; - char fname[1024]; - - orbiter = ob; - Efirst = Elast = 0; - timer = 0; - - for (i = strlen(ScnName)-1; i > 0; i--) - if (ScnName[i-1] == '\\') break; - sprintf (fname, "Flights\\%s\\system.dat", ScnName+i); - - sysfname = new char[strlen(fname)+1]; TRACENEW - strcpy (sysfname, fname); - - hDlg = OpenDialog(); - hEdit = NULL; - - ScanEventFile (); - RefreshEventList (); - InsertTMarker (); } -PlaybackEditor::~PlaybackEditor () +void NoteoffEvent::TagStr (char *str) { - if (hEdit) { - CommitEdit(); - DestroyWindow (hEdit); - } - CloseDialog(); - if (timer) KillTimer (hDlg, timer); + strcpy (str, "NOTEOFF"); } -HWND PlaybackEditor::OpenDialog () +void NoteoffEvent::DescStr (char *str) { - return orbiter->OpenDialogEx (IDD_RECPLAY_ANNOTATE, RecPlayAnn_DlgProc, DLG_CAPTIONCLOSE | DLG_CAPTIONHELP, this); + str[0] = '\0'; } -void PlaybackEditor::CloseDialog () +void NoteoffEvent::Write (ofstream &ofs) { - if (hDlg) { - orbiter->CloseDialog (hDlg); - hDlg = 0; - } + WriteEvent (ofs, "NOTEOFF", ""); } -HWND PlaybackEditor::OpenEditTab (PlaybackEvent *event, int resid, DLGPROC tabproc) -{ - if (hEdit) { - RegisterEdit (hEdit); // save current edits - DestroyWindow (hEdit); // remove current edit tab - } - if (resid) { - hEdit = CreateDialogParam (orbiter->GetInstance(), MAKEINTRESOURCE(resid), hDlg, tabproc, (LPARAM)event); - SetWindowLongPtr (hEdit, GWLP_USERDATA, (LONG_PTR)event); - } else { - hEdit = NULL; - } - return hEdit; +void NoteoffEvent::DrawPreview() { + ImGui::Text ("---", size); } -void PlaybackEditor::RegisterEdit (HWND hEdit) -{ - PlaybackEvent *e = (PlaybackEvent*)GetWindowLongPtr (hEdit, GWLP_USERDATA); - if (e) e->CommitEdit(); - int idx = SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETCURSEL, 0, 0); - if (idx != LB_ERR) - e = (PlaybackEvent*)SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETITEMDATA, idx, 0); - else e = 0; - RefreshEventList (e); - InsertTMarker(); +void NoteoffEvent::DrawEdit() { + PlaybackEvent::DrawEdit(); } - -INT_PTR PlaybackEditor::DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_INITDIALOG: { - INT tabs[2] = {40, 80}; - SendDlgItemMessage (hDlg, IDC_LIST1, LB_SETTABSTOPS, 2, (LPARAM)tabs); - CreateInsertEventList (hDlg); - timer = SetTimer (hDlg, 1, 100, NULL); - } return 0; - case WM_TIMER: - RefreshTMarker(); - break; - case WM_COMMAND: - switch (LOWORD (wParam)) { - case IDHELP: { - extern HELPCONTEXT DefHelpContext; - DefHelpContext.topic = (char*)"/playbackedit.htm"; - orbiter->OpenHelp (&DefHelpContext); - } return TRUE; - case IDCANCEL: - orbiter->CloseDialog (hDlg); - orbiter->FRecorder_ToggleEditor(); - return FALSE; - case IDC_BUTTON1: // delete event - DeleteEvent(); - break; - case IDC_BUTTON2: // insert event - InsertEvent(); - break; - case IDC_BUTTON3: // commit edits - SaveEventFile(); - break; - case IDC_LIST1: - switch (HIWORD (wParam)) { - case LBN_SELCHANGE: { - int idx = SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETCURSEL, 0, 0); - if (idx != LB_ERR && idx != tmarkidx) { - PlaybackEvent *e = (PlaybackEvent*)SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETITEMDATA, idx, 0); - if (e) e->EditEvent (this); - } - } break; - } - break; - } - break; - } - return OrbiterDefDialogProc (hDlg, uMsg, wParam, lParam); -} - -void PlaybackEditor::CreateInsertEventList (HWND hDlg) -{ - const int nevent = 7; - static const char *events[nevent] = { - "NOTE (annotation text)", - "NOTEPOS (bounding box)", - "NOTECOL (text colour)", - "NOTESIZE (text size)", - "TACC (time acceleration)", - "CAMERA (view position)", - "" - }; - SendDlgItemMessage (hDlg, IDC_COMBO1, CB_RESETCONTENT, 0, 0); - for (int i = 0; i < nevent; i++) - SendDlgItemMessage (hDlg, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)events[i]); - SendDlgItemMessage (hDlg, IDC_COMBO1, CB_SETCURSEL, 0, 0); -} - -void PlaybackEditor::ScanEventFile () -{ - char line[2048]; - ifstream ifs (sysfname); - while (ifs.getline (line, 2048)) { - PlaybackEvent *pe = PlaybackEvent::Create (this, line); - if (pe) { - Event *event = new Event; TRACENEW - event->e = pe; - event->next = NULL; - if (Elast) Elast->next = event; - else Efirst = event; - Elast = event; - } - } -} - -void PlaybackEditor::SaveEventFile () -{ - CommitEdit(); // commit last edits - orbiter->FRecorder_SuspendPlayback(); - ofstream ofs (sysfname); - Event *ev; - for (ev = Efirst; ev; ev = ev->next) { - ev->e->Write (ofs); - } - ofs.close(); - orbiter->FRecorder_RescanPlayback(); +void NoteoffEvent::ApplyChanges() { + PlaybackEvent::ApplyChanges(); } - -void PlaybackEditor::CommitEdit () -{ - PlaybackEvent *e = NULL; - if (hEdit) - e = (PlaybackEvent*)GetWindowLongPtr (hEdit, GWLP_USERDATA); - if (e) { - e->CommitEdit(); - RefreshEventList (e); - InsertTMarker(); - } -} - -void PlaybackEditor::RefreshEventList (PlaybackEvent *emark) -{ - SendDlgItemMessage (hDlg, IDC_LIST1, LB_RESETCONTENT, 0, 0); - Event *event; - char cbuf[1024], tstr[64], tagstr[64], descstr[1024]; - int idx; - for (event = Efirst; event; event = event->next) { - event->e->TimeStr (tstr); - event->e->TagStr (tagstr); - event->e->DescStr (descstr); - sprintf (cbuf, "%s\t%s\t%s", tstr, tagstr, descstr); - idx = SendDlgItemMessage (hDlg, IDC_LIST1, LB_ADDSTRING, 0, (LPARAM)cbuf); - SendDlgItemMessage (hDlg, IDC_LIST1, LB_SETITEMDATA, idx, (LPARAM)event->e); - if (event->e == emark) - SendDlgItemMessage (hDlg, IDC_LIST1, LB_SETCURSEL, idx, 0); - } -} - -int PlaybackEditor::InsertTMarker () -{ - int i; - char cbuf[1024]; - char tstr[1024]; - sprintf (tstr, "%0.1f\t< < < < < < < < < < < < < < < < < < <", td.SimT0); - double t; - for (i = 0;; i++) { - if (SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETTEXT, i, (LPARAM)cbuf) == LB_ERR) - break; - if (sscanf (cbuf, "%lf", &t) != 1) continue; - if (t >= td.SimT0) { - SendDlgItemMessage (hDlg, IDC_LIST1, LB_INSERTSTRING, i, (LPARAM)tstr); - tmarkidx = i; - return i; - } - } - return -1; -} - -void PlaybackEditor::RefreshTMarker () -{ - char cbuf[1024]; - char tstr[1024]; - double t, dummy; - if (orbiter->IsRunning()) { - bool blink = (modf (td.SysT0, &dummy) > 0.5); - if (blink) sprintf (tstr, "%0.0f", td.SimT0); - else sprintf (tstr, "%0.0f\t< < < < < < < < < < < < < < < < < < <", td.SimT0); - } else { - sprintf (tstr, "%0.2f\t< < < < < < < < < < < < < < < < < < < [Paused]", td.SimT0); - } - int i, topidx; - for (i = tmarkidx+1;; i++) { - if (SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETTEXT, i, (LPARAM)cbuf) == LB_ERR) - break; - if (sscanf (cbuf, "%lf", &t) != 1) continue; - if (t >= td.SimT0) break; - } - bool replace = (tmarkidx != i-1); - if (!replace) { - SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETTEXT, tmarkidx, (LPARAM)cbuf); - replace = (strcmp (cbuf, tstr) != 0); - } - if (replace) { - topidx = SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETTOPINDEX, 0, 0); - SendDlgItemMessage (hDlg, IDC_LIST1, LB_DELETESTRING, tmarkidx, 0); - SendDlgItemMessage (hDlg, IDC_LIST1, LB_INSERTSTRING, tmarkidx = i-1, (LPARAM)tstr); - SendDlgItemMessage (hDlg, IDC_LIST1, LB_SETTOPINDEX, topidx, 0); - } - if (orbiter->IsRunning()) { - topidx = SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETTOPINDEX, 0, 0); - if (topidx < max (tmarkidx-14, 0)) { - SendDlgItemMessage (hDlg, IDC_LIST1, LB_SETTOPINDEX, max (0, tmarkidx-14), 0); - } else if (topidx > tmarkidx-4) { - SendDlgItemMessage (hDlg, IDC_LIST1, LB_SETTOPINDEX, tmarkidx-4, 0); - } - } -} - -void PlaybackEditor::InsertEvent () -{ - PlaybackEvent *e = 0; - const double eps = 1e-3; - double newtime = td.SimT0-eps; // make sure the event is read when scanning the event list to current time - char cbuf[1024], eventstr[1024]; - GetWindowText (GetDlgItem (hDlg, IDC_COMBO1), cbuf, 1024); - sscanf (cbuf, "%s", eventstr); - if (!_stricmp (eventstr, "NOTE")) { - e = new NoteEvent (this, newtime, ""); TRACENEW - } else if (!_stricmp (eventstr, "NOTEPOS")) { - e = new NoteposEvent (this, newtime, 0, 0, 1, 1); TRACENEW - } else if (!_stricmp (eventstr, "NOTECOL")) { - e = new NotecolEvent (this, newtime, 1, 1, 1); TRACENEW - } else if (!_stricmp (eventstr, "NOTESIZE")) { - e = new NotesizeEvent (this, newtime, 1); TRACENEW - } else if (!_stricmp (eventstr, "TACC")) { - e = new TaccEvent (this, newtime, 1, 0); TRACENEW - } else if (!_stricmp (eventstr, "CAMERA")) { - e = new CameraEvent (this, newtime); TRACENEW - } else if (!_stricmp (eventstr, "")) { - e = new GenericEvent (this, newtime, "", ""); TRACENEW - } - if (e) { - Event *ev, *pev = 0, *event = new Event; TRACENEW - event->e = e; - event->next = NULL; - if (!Efirst) { - Efirst = event; - } else { - for (ev = Efirst; ev; ev = ev->next) { - if (ev->e->T0() > event->e->T0()) { - if (pev) { - pev->next = event; - } else { - Efirst = event; - } - event->next = ev; - break; - } - pev = ev; - } - if (!ev) { // add event to end of list - event->next = NULL; - if (pev) pev->next = event; - Elast->next = event; - } - } - if (!event->next) Elast = event; - } - RefreshEventList (e); - InsertTMarker (); - if (e) e->EditEvent (this); -} - -void PlaybackEditor::DeleteEvent () -{ - LRESULT idx = SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETCURSEL, 0, 0); - if (idx == LB_ERR || idx == tmarkidx) return; - - LRESULT res = SendDlgItemMessage (hDlg, IDC_LIST1, LB_GETITEMDATA, idx, 0); - if (res == LB_ERR || res == 0) return; - - PlaybackEvent *e = (PlaybackEvent*)res; - Event *ev, *pev = 0; - for (ev = Efirst; ev; ev = ev->next) { - if (ev->e == e) { - if (hEdit) { - DestroyWindow (hEdit); - hEdit = NULL; - } - if (pev) pev->next = ev->next; - if (Efirst == ev) Efirst = ev->next; - if (Elast == ev) Elast = pev; - delete ev->e; - delete ev; - RefreshEventList (); - InsertTMarker (); - break; - } - pev = ev; - } -} - -void PlaybackEditor::SortEvent (PlaybackEvent *e) -{ - Event *event, *ev, *pev = 0; - bool needsort = false; - for (ev = Efirst; ev; ev = ev->next) { - if (ev->e == e) { - if (pev && pev->e->T0() > e->T0()) { needsort = true; break; } - if (ev->next && ev->next->e->T0() < e->T0()) { needsort = true; break; } - } - pev = ev; - } - if (needsort) { - event = ev; - // prise the event out of the list - if (pev) pev->next = ev->next; - if (Efirst == ev) Efirst = ev->next; - if (Elast == ev) Elast = pev; - // and re-insert in new place - event->next = NULL; - for (ev = Efirst, pev = 0; ev; ev = ev->next) { - if (ev->e->T0() > e->T0()) { - if (pev) pev->next = event; - else Efirst = event; - event->next = ev; - break; - } - pev = ev; - } - if (!event->next) Elast = event; - } -} - -// ====================================================================== - -INT_PTR CALLBACK RecPlayAnn_DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - PlaybackEditor *pe = g_pOrbiter->FReditor; - if (uMsg == WM_INITDIALOG) { - pe = (PlaybackEditor*)lParam; - } - if (pe) return pe->DlgProc (hDlg, uMsg, wParam, lParam); - else return OrbiterDefDialogProc (hDlg, uMsg, wParam, lParam); -} - diff --git a/Src/Orbiter/PlaybackEd.h b/Src/Orbiter/PlaybackEd.h index dd6fc5ea8..c47cb6477 100644 --- a/Src/Orbiter/PlaybackEd.h +++ b/Src/Orbiter/PlaybackEd.h @@ -5,15 +5,34 @@ #define __PLAYBACKED_H #include "Orbiter.h" +#include "imgui.h" class PlaybackEditor; // ========================================================= +class PlaybackEvent; +class DlgPlaybackEditor : public ImGuiDialog { +public: + DlgPlaybackEditor(); + ~DlgPlaybackEditor(); + void OnDraw() override; + + void Load(const char *scenario); + void ScanEventFile(); + void SaveEventFile(); + char *m_sysfname; // system event file name + std::list m_Events; + PlaybackEvent *m_SelectedEvent; + + void ClearEvents(); + void InsertEvent(PlaybackEvent *); + void DeleteSelectedEvent(); +}; class PlaybackEvent { public: - static PlaybackEvent *Create (PlaybackEditor *editor, char *event); - PlaybackEvent (PlaybackEditor *_editor, double _t0); + static PlaybackEvent *Create (char *event); + PlaybackEvent (double _t0); virtual ~PlaybackEvent () {} inline double T0() const { return t0; } void TimeStr (char *str); @@ -21,12 +40,11 @@ class PlaybackEvent { virtual void DescStr (char *str) { str[0] = '\0'; } virtual void Write (std::ofstream &ofs) = 0; void WriteEvent (std::ofstream &ofs, const char *eventtype, const char *event); - virtual void EditEvent (PlaybackEditor *editor); - virtual void CommitEdit (); - virtual INT_PTR MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - + virtual void DrawPreview() = 0; + virtual void DrawEdit(); + virtual void ApplyChanges(); + char m_tmp_t0[32]; protected: - PlaybackEditor *editor; double t0; // event start time }; @@ -34,17 +52,15 @@ class PlaybackEvent { class GenericEvent: public PlaybackEvent { public: - GenericEvent (PlaybackEditor *_editor, double _t0, const char *_tag, const char *_content); + GenericEvent (double _t0, const char *_tag, const char *_content); ~GenericEvent (); void SetTag (const char *_tag); void SetContent (const char *_content); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - INT_PTR MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; private: char *tag; @@ -55,153 +71,132 @@ class GenericEvent: public PlaybackEvent { class TaccEvent: public PlaybackEvent { public: - TaccEvent (PlaybackEditor *_editor, double _t0, double _tacc, double _delay = 0.0); + TaccEvent (double _t0, double _tacc, float _delay = 0.0); inline double Tacc() const { return tacc; } - void SetTacc (double _tacc); - void SetDelay (double _delay); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - INT_PTR MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: double tacc; // time acceleration factor double delay; // delay time + float tmp_tacc; + char tmp_delay[20]; }; // ========================================================= class CameraEvent: public PlaybackEvent { public: - CameraEvent (PlaybackEditor *_editor, double _t0, DWORD _preset = (DWORD)-1); - CameraEvent (PlaybackEditor *_editor, double _t0, char *_modestr); - inline DWORD Preset() const { return preset; } - void SetPreset (DWORD _preset); - void SetInlineMode (char *mode); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - INT_PTR MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + CameraEvent (double _t0, int _preset = -1); + CameraEvent (double _t0, char *_modestr); + void SetPreset (int _preset, bool editmode = false); + void SetInlineMode (char *mode, bool editmode = false); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: - void ScanPresets (HWND hTab); - void AddCurrentView (HWND hTab); - DWORD preset; char modestr[256]; + char m_tmp_modestr[256]; }; // ========================================================= class NoteEvent: public PlaybackEvent { public: - NoteEvent (PlaybackEditor *_editor, double _t0, const char *_note); + NoteEvent (double _t0, const char *_note); ~NoteEvent (); - void SetNote (const char *_note); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: char *note; + char m_tmp_note[2048]; }; // ========================================================= class NoteposEvent: public PlaybackEvent { public: - NoteposEvent (PlaybackEditor *_editor, double _t0, double _x0, double _y0, double _x1, double _y1); - void SetPos (double _x0, double _y0, double _x1, double _y1); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + NoteposEvent (double _t0, double _x0, double _y0, double _x1, double _y1); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: double x0, y0, x1, y1; + ImVec2 m_offsetPos = {0,0}; + ImVec2 m_offsetSize = {0,0}; + int m_state = 0; + double m_tmp_x0; + double m_tmp_y0; + double m_tmp_x1; + double m_tmp_y1; }; // ========================================================= class NotecolEvent: public PlaybackEvent { public: - NotecolEvent (PlaybackEditor *_editor, double _t0, double _r, double _g, double _b); - void SetCol (double _r, double _g, double _b); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - INT_PTR MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + NotecolEvent (double _t0, double _r, double _g, double _b); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: double r, g, b; + float m_tmp[3]; }; // ========================================================= class NotesizeEvent: public PlaybackEvent { public: - NotesizeEvent (PlaybackEditor *_editor, double _t0, double _size); - void SetSize (double _size); - void TagStr (char *str); - void DescStr (char *str); - void Write (std::ofstream &ofs); - void EditEvent (PlaybackEditor *editor); - void CommitEdit (); - INT_PTR MsgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - static INT_PTR CALLBACK EditProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + NotesizeEvent (double _t0, double _size); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: double size; + char m_tmp_size[20]; }; -// ========================================================= // ========================================================= -class PlaybackEditor { +class NoteoffEvent: public PlaybackEvent { public: - PlaybackEditor (Orbiter *ob, const char *ScnName); - ~PlaybackEditor (); - INT_PTR DlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - HWND OpenEditTab (PlaybackEvent *event, int resid, DLGPROC tabproc); - HWND EditTab () const { return hEdit; } - void SortEvent (PlaybackEvent *e); + NoteoffEvent (double _t0); + void TagStr (char *str) override; + void DescStr (char *str) override; + void Write (std::ofstream &ofs) override; + void DrawPreview() override; + void DrawEdit() override; + void ApplyChanges() override; private: - HWND OpenDialog (); - void CloseDialog (); - void ScanEventFile (); - void SaveEventFile (); - void RefreshEventList (PlaybackEvent *emark = 0); - int InsertTMarker (); - void RefreshTMarker (); - void CreateInsertEventList (HWND hDlg); - void InsertEvent (); - void DeleteEvent (); - void CommitEdit (); - void RegisterEdit (HWND hEdit); - Orbiter *orbiter; - char *sysfname; // system event file name - HWND hDlg; // editor dialog handle - HWND hEdit; // currently displayed edit tab - UINT timer; // timer identifier - int tmarkidx; // list index of "current time" marker - struct Event { - PlaybackEvent *e; - struct Event *next; - } *Efirst, *Elast; // linked list of events + double size; + char m_tmp_size[20]; }; -#endif // !__PLAYBACKED_H \ No newline at end of file +#endif // !__PLAYBACKED_H diff --git a/Src/Orbiter/SDLWrappers.cpp b/Src/Orbiter/SDLWrappers.cpp new file mode 100644 index 000000000..5fbb3d676 --- /dev/null +++ b/Src/Orbiter/SDLWrappers.cpp @@ -0,0 +1,73 @@ +#define OAPI_IMPLEMENTATION +#include "SDL3/SDL_oapi.h" + +#include +#include + +DLLEXPORT sdl::ManagedWindow::ManagedWindow(const std::string_view title, const int width, + const int height, + const SDL_WindowFlags flags) { + m_inner = SDL_CreateWindow(title.data(), width, height, flags); + if (m_inner == nullptr) { + LOGOUT_ERR("SDL_CreateWindow() failed: %s", SDL_GetError()); + throw std::runtime_error("Failed to create window"); + } + +#ifdef _DEBUG + m_device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | + SDL_GPU_SHADERFORMAT_DXIL | + SDL_GPU_SHADERFORMAT_METALLIB, + true, nullptr); +#else + m_device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | + SDL_GPU_SHADERFORMAT_DXIL | + SDL_GPU_SHADERFORMAT_METALLIB, + false, nullptr); +#endif + if (m_device == nullptr) { + LOGOUT_ERR("SDL_CreateGPUDevice() failed: %s", SDL_GetError()); + throw std::runtime_error("Failed to create GPU device"); + } + + if (!SDL_ClaimWindowForGPUDevice(m_device, m_inner)) { + LOGOUT_ERR("SDL_ClaimWindowForGPUDevice() failed: %s", SDL_GetError()); + SDL_DestroyGPUDevice(m_device); + SDL_DestroyWindow(m_inner); + throw std::runtime_error("Failed to claim window"); + } + + if (!SDL_SetGPUSwapchainParameters(m_device, m_inner, + SDL_GPU_SWAPCHAINCOMPOSITION_SDR, + SDL_GPU_PRESENTMODE_VSYNC)) { + LOGOUT_ERR("SDL_SetGPUSwapchainParameters() failed: %s", + SDL_GetError()); + SDL_ReleaseWindowFromGPUDevice(m_device, m_inner); + SDL_DestroyGPUDevice(m_device); + SDL_DestroyWindow(m_inner); + throw std::runtime_error("Failed to set swapchain parameters"); + }; +} + +DLLEXPORT sdl::ManagedWindow::~ManagedWindow() { + if (m_device && m_inner) + SDL_ReleaseWindowFromGPUDevice(m_device, m_inner); + if (m_device) + SDL_DestroyGPUDevice(m_device); + if (m_inner) + SDL_DestroyWindow(m_inner); +} + +DLLEXPORT sdl::UnmanagedWindow::UnmanagedWindow(const std::string_view title, + const int width, const int height, + const SDL_WindowFlags flags) { + m_inner = SDL_CreateWindow(title.data(), width, height, flags); + if (m_inner == nullptr) { + LOGOUT_ERR("SDL_CreateWindow() failed: %s", SDL_GetError()); + throw std::runtime_error("Failed to create window"); + } +} + +DLLEXPORT sdl::UnmanagedWindow::~UnmanagedWindow() { + if (m_inner) + SDL_DestroyWindow(m_inner); +} \ No newline at end of file diff --git a/Src/Orbiter/Select.cpp b/Src/Orbiter/Select.cpp index e7bd09aec..ddb7513cf 100644 --- a/Src/Orbiter/Select.cpp +++ b/Src/Orbiter/Select.cpp @@ -1,855 +1,184 @@ // Copyright (c) Martin Schweiger // Licensed under the MIT License -#define STRICT 1 - -#include -#include -#include "Orbiter.h" -#include "Config.h" #include "Select.h" -#include "Util.h" -#include "Log.h" - -using std::max; - -extern Orbiter *g_pOrbiter; -extern char DBG_MSG[256]; -COLORREF titlecol = RGB(255,255,255); -COLORREF enablecol = RGB(210,210,210); -COLORREF disablecol = RGB(130,130,130); -COLORREF enablehi = RGB(255,255,255); -COLORREF disablehi = RGB(150,150,150); -COLORREF markcol = RGB(96,96,96); - -// ======================================================================= -// local prototypes - -//SURFHANDLE AllocSurface (oapi::GraphicsClient *gc, int w, int h); -void ReleaseSurface (LPDIRECTDRAWSURFACE7 &surf); -void ClearSurface (oapi::GraphicsClient *gc, SURFHANDLE surf, int h = 0); - -// ======================================================================= -// Base class for inline dialog box resources - -struct InlineDialog::DRAWRESRC InlineDialog::draw = {0,0,0,0,0}; - -InlineDialog::InlineDialog (oapi::GraphicsClient *gclient, HWND hwnd) -{ - gc = gclient; - hWnd = hwnd; -}; - -InlineDialog::~InlineDialog() -{ -} - -void InlineDialog::GlobalInit (oapi::GraphicsClient *gclient) -{ - if (!gclient) return; // sanity check - - draw.pen1 = gclient->clbkCreatePen (1, 0, 0x808080); - draw.pen2 = gclient->clbkCreatePen (1, 0, 0xFFFFFF); - draw.brushMark = gclient->clbkCreateBrush (markcol); - - DWORD viewH = g_pOrbiter->ViewH(); - double scale = g_pOrbiter->Cfg()->CfgFontPrm.dlgFont_Scale; - char *face1 = g_pOrbiter->Cfg()->CfgFontPrm.dlgFont1_Face; - - draw.fontH = (int)(viewH*0.02*scale); - if (draw.fontH < 8) draw.fontH = 8; - draw.fontNorm = gclient->clbkCreateFont (draw.fontH, true, face1); - draw.fontInactive = gclient->clbkCreateFont (draw.fontH, true, face1, FONT_ITALIC); - draw.fontInput = gclient->clbkCreateFont (draw.fontH, false, "Courier New"); -} - -void InlineDialog::GlobalExit (oapi::GraphicsClient *gclient) -{ - if (draw.pen1) gclient->clbkReleasePen (draw.pen1); - if (draw.pen2) gclient->clbkReleasePen (draw.pen2); - if (draw.brushMark) gclient->clbkReleaseBrush (draw.brushMark); - if (draw.fontNorm) gclient->clbkReleaseFont (draw.fontNorm); - if (draw.fontInactive) gclient->clbkReleaseFont (draw.fontInactive); - if (draw.fontInput) gclient->clbkReleaseFont (draw.fontInput); -} - -// ======================================================================= -// menu dialog box - -Select::Select (oapi::GraphicsClient *gclient, HWND hwnd): InlineDialog (gclient, hwnd) -{ - alen = llen = level = 0; - cbk_submenu = 0; - cbk_enter = 0; - cntx = cnty = -1; - surf = 0; - Clear (); - lineh = draw.fontH; - arrow = lineh/3; -} - -Select::~Select () -{ - if (alen) { - for (int i = 0; i < alen; i++) { - delete []buf[i]; - buf[i] = NULL; - } - delete []buf; - buf = NULL; - delete []buflen; - buflen = 0; - } - if (llen) { - for (int i = 0; i < llen; i++) { - delete []lbuf[i]; - lbuf[i] = NULL; - } - delete []lbuf; - lbuf = NULL; - delete []lbuflen; - lbuflen = 0; - delete []lcur; - lcur = 0; - } -} - -void Select::Activate () -{ - DWORD viewW = g_pOrbiter->ViewW(), viewH = g_pOrbiter->ViewH(); - int i, colh, itemh; - bool submenu = false; - DWORD textW; - - // remove trailing separator - buf[len-1][0] &= ~ITEM_SEPARATOR; - - // find width of longest entry - oapi::Sketchpad *skp = gc->clbkGetSketchpad (NULL); - if (skp) { - oapi::Font *nfnt, *ofnt = 0; - - colh = 0; - surfw = lineh; // left and right frame borders - surfh = 0; - ncol = 0; - col[ncol].nitem = 0; - col[ncol].w = 0; - for (i = 0; i < len; i++) { - nfnt = (buf[i][0] & ITEM_NOHILIGHT ? draw.fontInactive : draw.fontNorm); - if (nfnt != ofnt) skp->SetFont (ofnt = nfnt); - textW = skp->GetTextWidth (buf[i]+1); - itemh = (buf[i][0] & ITEM_SEPARATOR ? lineh+3 : lineh); - if (colh + itemh + 2*lineh > viewH) { // new column - if (submenu) col[ncol].w += lineh/2+arrow; - surfw += col[ncol].w + lineh; // column and vertical divider - ncol++; - col[ncol].w = textW; - surfh = max (surfh, colh); - colh = itemh; - submenu = false; - } else { - col[ncol].w = max (col[ncol].w, (int)textW); - colh += itemh; - } - col[ncol].nitem++; - if (buf[i][0] & ITEM_SUBMENU) submenu = true; - } - if (submenu) col[ncol].w += lineh/2+arrow; - surfw += col[ncol].w; - surfh = max (surfh, colh) + lineh + 4; - - if (ofnt != draw.fontNorm) skp->SetFont (draw.fontNorm); - textW = skp->GetTextWidth (title); - if (textW+lineh > surfw) surfw = textW+lineh; - gc->clbkReleaseSketchpad (skp); - } - - X0 = (cntx >= 0 ? cntx : viewW/2) - surfw/2; - if (X0 < 0) X0 = 0; - else if (X0+surfw >= viewW) X0 = viewW-surfw-1; - Y0 = (cnty >= 0 ? cnty : viewH/2) - surfh/2; - if (Y0 < 0) Y0 = 0; - else if (Y0+surfh >= viewH) Y0 = viewH-surfh-1; - - // allocate and init surface - surf = gc->clbkCreateSurface (surfw, surfh); - RefreshSurface (); - SetFocus (g_pOrbiter->GetRenderWnd()); -} - -void Select::Open (const char *_title, Callbk submenu_cbk, Callbk enter_cbk, - void *_userdata, int _cntx, int _cnty) -{ - if (surf) Clear (true); // this line was commented. why? - SetTitle (_title); - SetSubmenuCallback (submenu_cbk); - SetEnterCallback (enter_cbk); - userdata = _userdata; - cntx = _cntx, cnty = _cnty; - submenu_cbk (this, 0, 0, userdata); - Activate (); -} - -void Select::Clear (bool resetlevel) -{ - len = 0; // keep buffered entries around - cur = 0; - listw = listh = 0; - if (resetlevel) level = 0; - if (surf) { - gc->clbkReleaseSurface (surf); - surf = 0; - } -} - -void Select::MarkItem (int item) -{ - gc->clbkFillSurface (surf, 0, item*lineh, surfw, lineh, - gc->clbkGetDeviceColour (0x80, 0x80, 0x80)); - //RECT r = {0, item*lineh, surfw, item*lineh+lineh}; - //DDBLTFX bltfx; - //ZeroMemory (&bltfx, sizeof(bltfx)); - //bltfx.dwSize = sizeof(bltfx); - //bltfx.dwFillColor = GetSurfColour (0x80,0x80,0x80); - //surf->Blt (&r, NULL, NULL, DDBLT_COLORFILL, &bltfx); -} - -void Select::Display (LPDIRECTDRAWSURFACE7 pdds) -{ - if (!surf) return; // sanity check - gc->clbkBlt (0, X0, Y0, surf, 0); -} - -void Select::RefreshSurface () -{ - if (!surf) return; // sanity check - ClearSurface (gc, surf, lineh); - - // draw list - int i, cl, item0, item1, ay, x = lineh/2, y = 2; - int subx = surfw-lineh/2-arrow; - oapi::Font *ofnt, *nfnt; - oapi::Sketchpad *skp = gc->clbkGetSketchpad (surf); - if (skp) { - skp->SetFont (ofnt = draw.fontNorm); - skp->SetBackgroundMode (oapi::Sketchpad::BK_TRANSPARENT); - skp->SetTextColor (titlecol); - skp->SetPen (draw.pen1); - skp->Text (x, y-1, title, strlen(title)), y += lineh; - cl = 0; - item0 = 0; item1 = col[0].nitem; - for (i = 0; i < len; i++) { - nfnt = (buf[i][0] & ITEM_NOHILIGHT ? draw.fontInactive : draw.fontNorm); - if (nfnt != ofnt) skp->SetFont (ofnt = nfnt); - if (i == item1) { - skp->Line (x + col[cl].w + lineh/2, lineh+2, x + col[cl].w + lineh/2, surfh); - x += col[cl].w + lineh; - subx += col[cl].w + lineh; - y = 2+lineh; - item0 = item1; - item1 += col[++cl].nitem; - } - if (i == cur) { - int x0 = x-lineh/2; - int x1 = (cl == ncol ? surfw : x0+col[cl].w+lineh); - oapi::Brush *pbrush = skp->SetBrush (draw.brushMark); - skp->Rectangle (x0+2, y, x1-2, y+lineh); - skp->SetBrush (pbrush); - } - if (i == cur) skp->SetTextColor (buf[i][0] & ITEM_NOHILIGHT ? disablehi : enablehi); - else skp->SetTextColor (buf[i][0] & ITEM_NOHILIGHT ? disablecol : enablecol); - skp->Text (x, y, buf[i]+1, strlen (buf[i]+1)); - - if (buf[i][0] & ITEM_SUBMENU) { // submenu indicator - ay = y+lineh/2; - skp->MoveTo (subx, ay-arrow); - if (i == cur) skp->SetPen (draw.pen2); - skp->LineTo (subx+arrow, ay); - skp->LineTo (subx, ay+arrow); - skp->LineTo (subx, ay-arrow); - if (i == cur) skp->SetPen (draw.pen1); - } - y += lineh; - if (buf[i][0] & ITEM_SEPARATOR) { // separator indicator - skp->Line (0, y+1, surfw, y+1); - y += 3; - } - } - skp->Rectangle (0, 0, surfw, surfh); - gc->clbkReleaseSketchpad (skp); - } -} - -int Select::Append (const char *_str, BYTE flag) -{ - if (len == alen) { // need to allocate new entry - char **ctmp = new char*[alen+1]; TRACENEW - memcpy (ctmp, buf, alen*sizeof(char*)); - if (alen) delete []buf; - buf = ctmp; - int *itmp = new int[alen+1]; TRACENEW - memcpy (itmp, buflen, alen*sizeof(int)); - if (alen) delete []buflen; - buflen = itmp; - buflen[alen++] = 0; - } - int nlen = strlen (_str)+2; // reserve space for flag and EOL - if (buflen[len] < nlen) { // need to (re)allocate space for entry - if (buflen[len]) delete []buf[len]; - buf[len] = new char[buflen[len] = nlen]; TRACENEW - } - memcpy (buf[len]+1, _str, nlen-1); - buf[len][0] = (char)flag; - listh += lineh; - if (flag & ITEM_SEPARATOR) listh += 3; - return len++; -} - -void Select::AppendSeparator () -{ - if (!len) return; - if (buf[len-1][0] & ITEM_SEPARATOR) return; // got a separator already - buf[len-1][0] |= ITEM_SEPARATOR; - listh += 3; -} - -void Select::SetTitle (const char *_title) -{ - strncpy(title, _title, select_strlen); - title[select_strlen - 1] = '\0'; -} - -void Select::Push () -{ - if (level == llen) { // grow stack - char **ctmp = new char*[llen+1]; TRACENEW - memcpy (ctmp, lbuf, llen*sizeof(char*)); - if (llen) delete []lbuf; - lbuf = ctmp; - int *itmp = new int[llen+1]; TRACENEW - memcpy (itmp, lbuflen, llen*sizeof(int)); - if (llen) delete []lbuflen; - lbuflen = itmp; - lbuflen[llen] = 0; - itmp = new int[llen+1]; TRACENEW - memcpy (itmp, lcur, llen*sizeof(int)); - if (llen) delete []lcur; - lcur = itmp; - llen++; - } - if (lbuflen[level] < buflen[cur]) { // grow stack entry - if (lbuflen[level]) delete lbuf[level]; - lbuf[level] = new char[lbuflen[level] = buflen[cur]]; TRACENEW - } - memcpy (lbuf[level], buf[cur], buflen[cur]); - lcur[level++] = cur; -} - -void Select::Pop () -{ - if (!level) return; // sanity check - cur = lcur[--level]; -} - -int Select::ConsumeKey (UINT uMsg, WPARAM wParam, WORD mod) -{ - if (!IsActive() || uMsg != WM_KEYDOWN || mod) return key_ignore; - - switch (wParam) { - case VK_RETURN: - if (!(buf[cur][0] & ITEM_NOHILIGHT) && - (!cbk_enter || cbk_enter (this, cur, buf[cur]+1, userdata))) { - Clear (true); - return key_ok; - } else return key_consume; - case VK_ESCAPE: - Clear (true); - return key_cancel; - case VK_DOWN: - if (cur < len-1) { - cur++; - RefreshSurface (); - } - return key_consume; - case VK_UP: - if (cur > 0) { - cur--; - RefreshSurface (); - } - return key_consume; - case VK_RIGHT: - if ((buf[cur][0] & ITEM_SUBMENU) && cbk_submenu) { - Push (); // push current level to stack - Clear (); - if (cbk_submenu (this, lcur[level-1], lbuf[level-1]+1, userdata)) { - Activate (); - return key_consume; - } - } else return key_ignore; - // fall through - case VK_LEFT: - if (level && cbk_submenu) { - Clear (); - Pop (); // get previous level from stack - if (level) cbk_submenu (this, lcur[level-1], lbuf[level-1]+1, userdata); - else cbk_submenu (this, 0, 0, userdata); // main menu - Activate (); - } - return key_consume; - default: - return key_ignore; - } -} - -// ======================================================================= - -SelectionList::SelectionList (oapi::GraphicsClient *gclient, HWND hwnd): InlineDialog (gclient, hwnd) -{ - clbk = 0; - userdata = 0; - listflag = 0; - list = 0; - nlist = 0; - title = 0; - lineh = draw.fontH; - arrow = lineh/3; - surf = 0; - cur = 0; -} - -void SelectionList::Open (LISTENTRY *_list, DWORD _nlist, char *_title, Listentry_clbk _clbk, - DWORD flag, void *_userdata) -{ - Clear (); - - clbk = _clbk; - userdata = _userdata; - listflag = flag; - list = new LISTENTRY[nlist = _nlist]; - memcpy (list, _list, nlist*sizeof(LISTENTRY)); - - if (_title) { - title = new char[strlen(_title)+1]; - strcpy (title, _title); - } - - AllocSurface(); -} - -void SelectionList::Clear () -{ - clbk = 0; - userdata = 0; - listflag = 0; - if (nlist) { - delete []list; - list = 0; - nlist = 0; - } - if (title) { - delete []title; - title = 0; - } - if (surf) { - gc->clbkReleaseSurface (surf); - surf = 0; - } -} - -void SelectionList::AllocSurface () -{ - DWORD viewW = g_pOrbiter->ViewW(), viewH = g_pOrbiter->ViewH(); - int colh, itemh; - DWORD i, textw; - bool submenu = false; - - // remove trailing separator - list[nlist-1].flag &= ~LISTENTRY_SEPARATOR; - - // find width of longest entry - oapi::Sketchpad *skp = gc->clbkGetSketchpad (NULL); - if (skp) { - oapi::Font *nfnt, *ofnt = 0; - colh = 0; - surfw = lineh; - surfh = 0; - ncol = 0; - col[ncol].nitem = 0; - col[ncol].w = 0; - for (i = 0; i < nlist; i++) { - nfnt = (list[i].flag & LISTENTRY_INACTIVE ? draw.fontInactive : draw.fontNorm); - if (nfnt != ofnt) skp->SetFont (ofnt = nfnt); - textw = skp->GetTextWidth (list[i].name); - itemh = (list[i].flag & LISTENTRY_SEPARATOR ? lineh+3 : lineh); - if (colh + itemh + 2*lineh > viewH) { // new column - if (submenu) col[ncol].w += lineh/2+arrow; - surfw += col[ncol].w + lineh; // column and vertical divider - ncol++; - col[ncol].w = textw; - surfh = max (surfh, colh); - colh = itemh; - submenu = false; - } else { - col[ncol].w = max ((DWORD)col[ncol].w, textw); - colh += itemh; - } - col[ncol].nitem++; - if (list[i].flag & LISTENTRY_SUBITEM) submenu = true; - } - if (submenu) col[ncol].w += lineh/2+arrow; - surfw += col[ncol].w; - surfh = max (surfh, colh) + lineh + 4; - - if (ofnt != draw.fontNorm) skp->SetFont (draw.fontNorm); - textw = skp->GetTextWidth (title); - if (textw+lineh > surfw) surfw = textw+lineh; - gc->clbkReleaseSketchpad (skp); - } - - surfx = viewW/2 - surfw/2; - if (surfx < 0) surfx = 0; - else if (surfx+surfw >= viewW) surfx = viewW-surfw-1; - surfy = viewH/2 - surfh/2; - if (surfy < 0) surfy = 0; - else if (surfy+surfh >= viewH) surfy = viewH-surfh-1; - - if (surf) gc->clbkReleaseSurface (surf); - surf = gc->clbkCreateSurface (surfw, surfh); -} - -void SelectionList::RefreshSurface () -{ - if (!surf) return; // sanity check - ClearSurface (gc, surf, lineh); - - // draw list - int i, cl, item0, item1, ay, x = lineh/2, y = 2; - int subx = surfw-lineh/2-arrow; - oapi::Font *ofnt, *nfnt; - oapi::Sketchpad *skp = gc->clbkGetSketchpad (surf); - if (skp) { - skp->SetFont (ofnt = draw.fontNorm); - skp->SetBackgroundMode (oapi::Sketchpad::BK_TRANSPARENT); - skp->SetTextColor (titlecol); - skp->SetPen (draw.pen1); - skp->Text (x, y-1, title, strlen(title)), y += lineh; - cl = 0; - item0 = 0; item1 = col[0].nitem; - for (i = 0; i < nlist; i++) { - nfnt = (list[i].flag & LISTENTRY_INACTIVE ? draw.fontInactive : draw.fontNorm); - if (nfnt != ofnt) skp->SetFont (ofnt = nfnt); - if (i == item1) { - skp->Line (x + col[cl].w + lineh/2, lineh+2, x + col[cl].w + lineh/2, surfh); - x += col[cl].w + lineh; - subx += col[cl].w + lineh; - y = 2+lineh; - item0 = item1; - item1 += col[++cl].nitem; - } - if (i == cur) { - int x0 = x-lineh/2; - int x1 = (cl == ncol ? surfw : x0+col[cl].w+lineh); - oapi::Brush *pbrush = skp->SetBrush (draw.brushMark); - skp->Rectangle (x0+2, y, x1-2, y+lineh); - skp->SetBrush (pbrush); - } - if (i == cur) skp->SetTextColor (list[i].flag & LISTENTRY_INACTIVE ? disablehi : enablehi); - else skp->SetTextColor (list[i].flag & LISTENTRY_INACTIVE ? disablecol : enablecol); - skp->Text (x, y, list[i].name, strlen (list[i].name)); - - if (list[i].flag & LISTENTRY_SUBITEM) { // submenu indicator - ay = y+lineh/2; - skp->MoveTo (subx, ay-arrow); - if (i == cur) skp->SetPen (draw.pen2); - skp->LineTo (subx+arrow, ay); - skp->LineTo (subx, ay+arrow); - skp->LineTo (subx, ay-arrow); - if (i == cur) skp->SetPen (draw.pen1); - } - y += lineh; - if (list[i].flag & LISTENTRY_SEPARATOR) { // separator indicator - skp->Line (0, y+1, surfw, y+1); - y += 3; - } - } - skp->Rectangle (0, 0, surfw, surfh); - gc->clbkReleaseSketchpad (skp); - } -} - -// ======================================================================= - -InputBox::InputBox (oapi::GraphicsClient *gclient, HWND hwnd, int _buflen): InlineDialog (gclient, hwnd) -{ - buflen = _buflen; - titlelen = 20; // can grow as required - title = new char[titlelen+1]; title[0] = '\0'; TRACENEW - buf = new char[buflen+1]; buf[0] = '\0'; TRACENEW - slen = vis0 = cur = 0; - vislen = 20; // arbitrary default - surf = 0; - cbk_enter = 0; - cbk_cancel = 0; - userdata = 0; - lineh = draw.fontH; - cw = g_gdires->dlgF2W; // replace this! -} - -InputBox::~InputBox () -{ - delete []buf; - buf = NULL; - delete []title; - title = NULL; -} - -void InputBox::SetTitle (const char *_title) -{ - int len = (_title ? strlen(_title) : 0); - if (len > titlelen) { - if (titlelen) delete []title; - title = new char[(titlelen=len)+1]; TRACENEW - } - if (len) strcpy (title, _title); - else title[0] = '\0'; -} - -void InputBox::InitBuffer (int _vislen, char *_buf) -{ - vislen = _vislen; - vis0 = 0; - if (_buf) { - slen = cur = strlen(_buf); - strcpy (buf, _buf); - } else { - slen = cur = 0; - buf[0] = '\0'; - } -} - -void InputBox::Activate (int cntx, int cnty) -{ - DWORD viewW = g_pOrbiter->ViewW(), viewH = g_pOrbiter->ViewH(); - DWORD textW; - oapi::Sketchpad *skp = gc->clbkGetSketchpad (NULL); - if (skp) { - skp->SetFont (draw.fontNorm); - textW = skp->GetTextWidth (title); - surfw = textW + lineh; - boxw = cw*vislen; - if (boxw+lineh > surfw) surfw = boxw+lineh; - surfh = 4*lineh; - gc->clbkReleaseSketchpad (skp); - } - X0 = (cntx >= 0 ? cntx : viewW/2) - surfw/2; - if (X0 < 0) X0 = 0; - else if (X0+surfw >= viewW) X0 = viewW-surfw-1; - Y0 = (cnty >= 0 ? cnty : viewH/2) - surfh/2; - if (Y0 < 0) Y0 = 0; - else if (Y0+surfh >= viewH) Y0 = viewH-surfh-1; - - // allocate and init surface - if (surf) gc->clbkReleaseSurface (surf); - surf = gc->clbkCreateSurface (surfw, surfh); - RefreshSurface (); -} - -void InputBox::Open (const char *_title, char *_buf, int _vislen, - Callbk cbk, void *_userdata, int cntx, int cnty) -{ - OpenEx (_title, _buf, _vislen, cbk, 0, _userdata, 0, cntx, cnty); -} - -bool InputBox::OpenEx (const char *_title, char *_buf, int _vislen, - Callbk enter_cbk, Callbk cancel_cbk, void *_userdata, DWORD flags, int cntx, int cnty) -{ - if (surf) { - if (flags & USRINPUT_NEEDANSWER) return false; - // can't open new input box if currently open one requires an answer - if (cbk_cancel) cbk_cancel (this, buf, userdata); - Close(); - } - SetTitle (_title); - InitBuffer (_vislen, _buf); - SetEnterCallback (enter_cbk); - cbk_cancel = cancel_cbk; - flag = flags; - userdata = _userdata; - Activate (cntx, cnty); - return true; -} - -bool InputBox::Close (bool onEnter) -{ - if (!onEnter && (flag & USRINPUT_NEEDANSWER)) return false; - if (surf) { - gc->clbkReleaseSurface (surf); - surf = 0; - } - userdata = 0; - return true; -} - -void InputBox::RefreshSurface () -{ - if (!surf) return; // sanity check - ClearSurface (gc, surf, lineh); - - oapi::Sketchpad *skp = gc->clbkGetSketchpad (surf); - if (skp) { - int x = lineh/2, y = 2; - skp->SetFont (draw.fontNorm); - skp->SetPen (draw.pen1); - skp->SetBackgroundMode (oapi::Sketchpad::BK_TRANSPARENT); - skp->SetTextColor (titlecol); - skp->Text (x, y-1, title, strlen(title)); y += 2*lineh; - skp->SetTextColor (enablecol); - skp->SetFont (draw.fontInput); - int n = slen-vis0; - if (n > vislen) n = vislen; - skp->Text (x, y, buf+vis0, n); - skp->SetBackgroundMode (oapi::Sketchpad::BK_OPAQUE); - skp->SetTextColor (0); - skp->Text (x+(cur-vis0)*cw, y, cur < slen ? buf+cur : " ", 1); - skp->Rectangle (0, 0, surfw, surfh); - skp->Rectangle (x-2, y-2, x+boxw+2, y+lineh+2); - gc->clbkReleaseSketchpad (skp); - } -} - -void InputBox::Display (LPDIRECTDRAWSURFACE7 pdds) -{ - if (!surf) return; // sanity check - gc->clbkBlt (0, X0, Y0, surf, 0); - //pdds->BltFast (X0, Y0, surf, NULL, DDBLTFAST_WAIT); -} - -int InputBox::ConsumeKey (UINT uMsg, WPARAM wParam, WORD mod) -{ - int i; - - if (!IsActive()) return key_ignore; - - switch (uMsg) { - case WM_CHAR: - if (isgraph(wParam) || wParam == VK_SPACE) { - if (slen < buflen) { - for (i = slen; i >= cur; i--) buf[i+1] = buf[i]; - buf[cur] = wParam; - slen++; - if (++cur == vislen+vis0) vis0++; - RefreshSurface(); - } - return key_consume; +#include "imgui.h" + +Select::Select():ImGuiDialog("Select") { + active = false; + opened = false; + title = "Selection"; + currententry = nullptr; +} + +void Select::Open(const char *_title, Callbk submenu_cbk, Callbk enter_cbk, void *_userdata, int cntx, int cnty) { + title = _title; + + cbSubmenu = submenu_cbk; + cbEnter = enter_cbk; + userdata = _userdata; + + rootmenu.clear(); + currententry = &rootmenu; + + submenu_cbk(this, 0, nullptr, _userdata); + opened = true; + active = true; +} + +void Select::Append(const char *str, int flags) { + SelectEntry e; + e.m_Flags = flags; + e.m_Text = str; + + currententry->emplace_back(e); +} + +void Select::AppendSeparator() { + SelectEntry e; + e.m_Flags = ITEM_SEPARATOR; + + currententry->emplace_back(e); +} + +void Select::DrawMenu(std::list& entries) { + int i = 0; + for (auto& e : entries) { + if (e.m_Flags & ITEM_SEPARATOR) { + ImGui::Separator(); + } + else if (e.m_Flags & ITEM_SUBMENU) { + if (e.m_SubEntries.size() == 0) { + currententry = &e.m_SubEntries; + cbSubmenu(this, i, const_cast(e.m_Text.c_str()), userdata); + } + if (ImGui::BeginMenu(e.m_Text.c_str(), e.m_SubEntries.size() != 0)) { + DrawMenu(e.m_SubEntries); + ImGui::EndMenu(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly) && !(e.m_Flags & ITEM_NOHILIGHT)) { + if (ImGui::IsMouseReleased(0)) { + cbEnter(this, i, const_cast(e.m_Text.c_str()), userdata); + active = false; + ImGui::CloseCurrentPopup(); + } + } + } + i++; + } + else { + if (ImGui::MenuItem(e.m_Text.c_str())) { + cbEnter(this, i, const_cast(e.m_Text.c_str()), userdata); + active = false; + } + i++; + } + } +} + +void Select::OnDraw() { + ImGuiIO& io = ImGui::GetIO(); + + if (io.MouseDown[0] && opened) { + return; + } + + // Open the context menu inside the focused viewport + // so it's not hidden by it in case it's opened from + // a secondary viewport but its draw area fits inside + // the main viewport + ImGuiPlatformIO& pio = ImGui::GetPlatformIO(); + for (ImGuiViewport *viewport : pio.Viewports) { + if(viewport->Flags & ImGuiViewportFlags_IsFocused) { + ImGui::SetNextWindowViewport(viewport->ID); + break; } - return key_ignore; - case WM_KEYDOWN: - if (!mod) { - switch (wParam) { - case VK_RETURN: - if (!cbk_enter || cbk_enter (this, buf, userdata)) { - Close (true); - return key_ok; - } else { - MessageBeep (-1); - return key_consume; - } - case VK_ESCAPE: - if (flag & USRINPUT_NEEDANSWER) { - return key_consume; - } else { - if (cbk_cancel) cbk_cancel (this, buf, userdata); - Close (); - return key_cancel; - } - case VK_LEFT: - if (cur > 0) { - if (--cur < vis0) vis0 = cur; - RefreshSurface(); - } - return key_consume; - case VK_RIGHT: - if (cur < slen) { - if (++cur == vislen+vis0) vis0++; - RefreshSurface(); - } - return key_consume; - case VK_BACK: - if (cur > 0) { - for (i = cur; i <= slen; i++) buf[i-1] = buf[i]; - slen--; - if (--cur < vis0) vis0 = cur; - RefreshSurface(); - } - return key_consume; - case VK_DELETE: - if (cur < slen) { - for (i = cur+1; i <= slen; i++) buf[i-1] = buf[i]; - slen--; - RefreshSurface(); - } - return key_consume; - case VK_HOME: - if (cur > 0) { - cur = 0; - if (vis0 > 0) vis0 = 0; - RefreshSurface(); - } - return key_consume; - case VK_END: - if (cur < slen) { - if ((cur = slen) >= vislen+vis0) vis0 = cur-vislen+1; - RefreshSurface(); - } - return key_consume; - default: - // trap unmodified and shift keys - if (!(mod & 0x02)) return key_consume; - break; - } - } - } - return key_ignore; -} - -// ======================================================================= -// auxiliary routines - -#ifdef UNDEF -SURFHANDLE AllocSurface (oapi::GraphicsClient *gc, int w, int h) -{ - return NULL; -} -#endif - -void ReleaseSurface (LPDIRECTDRAWSURFACE7 &surf) -{ - if (surf) { - surf->Release (); - surf = 0; } -} -void ClearSurface (oapi::GraphicsClient *gc, SURFHANDLE surf, int h) -{ - gc->clbkFillSurface (surf, gc->clbkGetDeviceColour (0x50, 0x50, 0x50)); - //DDBLTFX bltfx; - //ZeroMemory (&bltfx, sizeof(bltfx)); - //bltfx.dwSize = sizeof(bltfx); - //bltfx.dwFillColor = GetSurfColour (0x50,0x50,0x50); - //surf->Blt (NULL, NULL, NULL, DDBLT_COLORFILL, &bltfx); - - if (h) { // paint title bar - DWORD width, height; - gc->clbkGetSurfaceSize (surf, &width, &height); - gc->clbkFillSurface (surf, 0, 0, width, h+1, - gc->clbkGetDeviceColour (0x60, 0x20, 0x20)); - //DDSURFACEDESC2 ddsd; - //ddsd.dwSize = sizeof(ddsd); - //surf->GetSurfaceDesc (&ddsd); - //RECT r = {0, 0, ddsd.dwWidth, h+1}; - //bltfx.dwFillColor = GetSurfColour (0x60,0x20,0x20); - //surf->Blt (&r, NULL, NULL, DDBLT_COLORFILL, &bltfx); - } + if (opened) { + ImGui::OpenPopup(title.c_str()); + opened = false; + } + + if (ImGui::BeginPopup(title.c_str())) + { + ImGui::TextUnformatted(title.c_str()); + ImGui::Separator(); + DrawMenu(rootmenu); + ImGui::EndPopup(); + } + else { + active = false; + } +} + +InputBox::InputBox():ImGuiDialog("InputBox") { + active = false; + opened = false; + title = "InputBox"; +} + +void InputBox::Open(const char *_title, char *_buf, int _vislen, + Callbk cbk, void *_userdata, int cntx, int cnty) +{ + OpenEx(_title, _buf, _vislen, cbk, 0, _userdata, 0, cntx, cnty); +} + +bool InputBox::OpenEx(const char *_title, char *_buf, int _vislen, + Callbk enter_cbk, Callbk cancel_cbk, void *_userdata, + int flags, int cntx, int cnty) { + + title = _title; + + cbEnter = enter_cbk; + cbCancel = cancel_cbk; + userdata = _userdata; + + opened = true; + active = true; + + if (_buf) + strcpy(inputbuf, _buf); + else + inputbuf[0] = '\0'; + + return true; +} + +void InputBox::OnDraw() { + char buf[256]; + sprintf(buf, "%s###InputBox", title.c_str()); + + bool firstTime = false; + if (opened) { + ImGui::OpenPopup(buf); + opened = false; + firstTime = true; + } + + if (ImGui::BeginPopup(buf)) + { + ImGui::TextUnformatted(title.c_str()); + ImGui::Separator(); + ImGui::SetNextItemWidth(-FLT_MIN); + if (firstTime) + ImGui::SetKeyboardFocusHere(); + bool entered = ImGui::InputText("##InputText", inputbuf, IM_ARRAYSIZE(inputbuf), ImGuiInputTextFlags_EnterReturnsTrue); + + if (ImGui::Button("OK") || entered) { + cbEnter(this, inputbuf, userdata); + ImGui::CloseCurrentPopup(); + active = false; + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + if (cbCancel) + cbCancel(this, inputbuf, userdata); + ImGui::CloseCurrentPopup(); + active = false; + } + ImGui::EndPopup(); + } + else { + active = false; + } } diff --git a/Src/Orbiter/Select.h b/Src/Orbiter/Select.h index f1f4c31ba..e4e6e009c 100644 --- a/Src/Orbiter/Select.h +++ b/Src/Orbiter/Select.h @@ -4,215 +4,65 @@ #ifndef __SELECT_H #define __SELECT_H -#define STRICT 1 -#include -#include #include "OrbiterAPI.h" -#include "GraphicsAPI.h" +#include #define ITEM_SUBMENU 0x01 #define ITEM_NOHILIGHT 0x02 #define ITEM_SEPARATOR 0x04 -const int select_strlen = 100; -const int select_chunk = 32; -const int select_maxlevel = 10; -const int maxcol = 20; - -// ======================================================================= -// Base class for inline dialog box resources - -class InlineDialog { -public: - InlineDialog (oapi::GraphicsClient *gclient, HWND hwnd); - virtual ~InlineDialog(); - - // initialise global drawing resources - static void GlobalInit (oapi::GraphicsClient *gclient); - - // release global drawing resources - static void GlobalExit (oapi::GraphicsClient *gclient); - -protected: - static struct DRAWRESRC { // global drawing resources - oapi::Pen *pen1, *pen2; - oapi::Brush *brushMark; - oapi::Font *fontNorm, *fontInactive, *fontInput; - int fontH; - } draw; - - oapi::GraphicsClient *gc; // graphics client instance - HWND hWnd; // render window handle +struct SelectEntry { + int m_Flags; + std::string m_Text; + std::list m_SubEntries; }; -// ======================================================================= -// menu dialog box - -class Select: public InlineDialog { +class Select : public ImGuiDialog { public: - Select (oapi::GraphicsClient *gclient, HWND hwnd); - ~Select (); - void Activate (); - void Clear (bool resetlevel = false); - void Display (LPDIRECTDRAWSURFACE7 pdds); - bool IsActive() { return (len > 0); } - int Len() const { return len; } - int Cur() const { return cur; } - char *Str (int i) const { return buf[i]+1; } - int ParentCur () const { return (level > 0 ? lcur[level-1] : 0); } - const char *ParentStr () const { return (level > 0 ? lbuf[level-1]+1 : 0); } - const char *StackStr (int lev) { return (lev >= 0 && lev < level ? lbuf[lev]+1 : 0); } - bool HasSubmenu (int i) const { return buf[i][0] & 0x01; } - int Level() const { return level; } - int Append (const char *_str, BYTE flag = 0); - void AppendSeparator (); - void SetTitle (const char *_title); - const char *Title () const { return title; } - int ConsumeKey (UINT uMsg, WPARAM wParam, WORD mod = 0); - enum { key_ignore, key_consume, key_ok, key_cancel }; - typedef bool (*Callbk)(Select*, int, char*, void*); - void SetSubmenuCallback (Callbk cbk) { cbk_submenu = cbk; } - void SetEnterCallback (Callbk cbk) { cbk_enter = cbk; } - - void Open (const char *_title = 0, Callbk submenu_cbk = 0, Callbk enter_cbk = 0, - void *_userdata = 0, int cntx = -1, int cnty = -1); - // activates the menu with the specified parameters (menu items must have - // been added with Append before) - -private: - void RefreshSurface (); - void MarkItem (int item); - void Push (); - void Pop (); - - SURFHANDLE surf; // drawing surface - int surfw, surfh; // surface dimensions - int cntx, cnty; // centre coords - int X0, Y0; // upper left corner of input box - int lineh; // font height - int arrow; // arrow size - int listw; // pixel width of longest list entry - int listh; // pixel height of full list (w/o title) - int len; // number of active entries - int alen; // number of allocated entries - struct COL { // column specs - int nitem; // # items in column - int w; // column width - } col[maxcol]; - int ncol; // # columns - int llen; // level stack size - int level; // current submenu level (0=top level) - int cur; // selected entry index - int *lcur; // entry index stack (length llen) - char **buf; // array of string buffers (length alen) - int *buflen; // array buffer sizes (length alen); - char **lbuf; // entry stack (length llen); - int *lbuflen; // array of stack entry sizes (length llen) - char title[select_strlen]; - void *userdata; - Callbk cbk_submenu; - Callbk cbk_enter; + typedef bool (*Callbk)(Select*, int, char*, void*); + Select(); + void Display() override { + OnDraw(); + if (!active) OnClose(); + } + + void OnDraw() override; + bool opened; + std::string title; + std::list rootmenu; + std::list *currententry; + Callbk cbSubmenu; + Callbk cbEnter; + void *userdata; + + void Open(const char *_title = 0, Callbk submenu_cbk = 0, Callbk enter_cbk = 0, void *_userdata = 0, int cntx = -1, int cnty = -1); + void Append(const char *str, int flags = 0); + void AppendSeparator(); + void DrawMenu(std::list& entries); }; -// ======================================================================= -// selection list - -class SelectionList: public InlineDialog { +class InputBox : public ImGuiDialog { public: - SelectionList (oapi::GraphicsClient *gclient, HWND hwnd); - - void Open (LISTENTRY *list, DWORD nlist, char *_title, Listentry_clbk _clbk, - DWORD flag = 0, void *_userdata = 0); - - void Clear(); - -private: - void AllocSurface(); - void RefreshSurface(); - - Listentry_clbk clbk; - void *userdata; - DWORD listflag; - LISTENTRY *list; // entries in selection list - DWORD nlist; // number of entries - char *title; - SURFHANDLE surf; // drawing surface - int surfw, surfh; // surface dimensions - int surfx, surfy; // display location - int lineh; // font height - int arrow; // arrow size - int cur; // selected entry index - int ncol; // number of columns - struct COL { // column specs - int nitem; // # items in column - int w; // column width - } col[maxcol]; -}; - -// ======================================================================= -// text input dialog box - -class InputBox: public InlineDialog { -public: - InputBox (oapi::GraphicsClient *gclient, HWND hwnd, int _buflen); - // construct an input box resource for hwnd, allocating - // an input buffer of size _buflen - - ~InputBox (); - - typedef bool (*Callbk)(InputBox*, char*, void*); - // template for callback function. Returns pointer to calling input box, - // input box string and user-defined data - - void SetTitle (const char *_title); - void InitBuffer (int _vislen, char *str); - - void Activate (int cntx = -1, int cnty = -1); - // activate intput box centered at position cntx, cnty (default is viewport centre) - - bool IsActive() { return (surf != 0); } - - void Display (LPDIRECTDRAWSURFACE7 pdds); - // copy input box onto viewport surface - - enum { key_ignore, key_consume, key_ok, key_cancel }; - - int ConsumeKey (UINT uMsg, WPARAM wParam, WORD mod = 0); - // Processes WM_CHAR and WM_KEYDOWN messages - - void SetEnterCallback (Callbk cbk) { cbk_enter = cbk; } - // set callback function for accepting input - - void Open (const char *_title = 0, char *_buf = 0, int _vislen = 20, - Callbk cbk = 0, void *_userdata = 0, int cntx = -1, int cnty = -1); - // activates the input box with the specified parameters - // combines SetTitle, InitBuffer, SetEnterCallback and Activate - - bool OpenEx (const char *_title = 0, char *_buf = 0, int _vislen = 20, - Callbk enter_cbk = 0, Callbk cancel_cbk = 0, void *_userdata = 0, - DWORD flags = 0, int cntx = -1, int cnty = -1); - // This version also provides a callback function for cancelling the - // input box - - bool Close (bool onEnter = false); - // de-activates the input box - -private: - void RefreshSurface (); - - SURFHANDLE surf; // drawing surface - int surfw, surfh; // surface dimensions - int X0, Y0; // upper left corner of input box - int lineh; // font height - int boxw; // input box width - int cw; // fixed character width - char *title, *buf; - int titlelen, buflen, vislen, vis0, slen; - int cur; // cursor position - void *userdata; - DWORD flag; - Callbk cbk_enter; - Callbk cbk_cancel; + typedef bool (*Callbk)(InputBox*, char*, void*); + InputBox(); + void Display() override { + OnDraw(); + if (!active) OnClose(); + } + void OnDraw() override; + bool opened; + std::string title; + Callbk cbEnter; + Callbk cbCancel; + void *userdata; + char inputbuf[128]; + + void Open(const char *_title = 0, char *_buf = 0, int _vislen = 20, + Callbk cbk = 0, void *_userdata = 0, int cntx = -1, int cnty = -1); + + bool OpenEx(const char *_title = 0, char *_buf = 0, int _vislen = 20, + Callbk enter_cbk = 0, Callbk cancel_cbk = 0, void *_userdata = 0, + int flags = 0, int cntx = -1, int cnty = -1); }; -#endif // !__SELECT_H \ No newline at end of file +#endif diff --git a/Src/Orbiter/TabOptions.h b/Src/Orbiter/TabOptions.h index 455bfeb31..9a864035d 100644 --- a/Src/Orbiter/TabOptions.h +++ b/Src/Orbiter/TabOptions.h @@ -11,8 +11,6 @@ #include "LpadTab.h" #include "OptionsPages.h" -#include "CustomControls.h" -#include "DlgOptions.h" namespace orbiter { diff --git a/Src/Orbiter/VCockpit.cpp b/Src/Orbiter/VCockpit.cpp index c6fe3b49a..e83fd4797 100644 --- a/Src/Orbiter/VCockpit.cpp +++ b/Src/Orbiter/VCockpit.cpp @@ -34,7 +34,7 @@ VirtualCockpit::VirtualCockpit (int _id, const Pane *_pane) if (g_pOrbiter->IsFullscreen()) cwnd = 0; else - cwnd = g_pOrbiter->GetRenderWnd(); + cwnd = g_pOrbiter->GetRenderWnd()->Win32Handle(); for (i = 0; i < 4; i++) connect[i] = -1; } @@ -346,23 +346,21 @@ bool VirtualCockpit::SetClickZone_Quadrilateral (int i, return true; } -bool VirtualCockpit::ProcessMouse (UINT event, DWORD state, int x, int y) +bool VirtualCockpit::ProcessMouse (const SDL_Event &event, int x, int y) { mstate = 0; - switch (event) { - case WM_LBUTTONDOWN: - state = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; - break; - case WM_RBUTTONDOWN: - state = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; - break; - case WM_LBUTTONUP: - state = PANEL_MOUSE_LBUP; - break; - case WM_RBUTTONUP: - state = PANEL_MOUSE_RBUP; - break; - } + auto state = 0; + + if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) { + state = PANEL_MOUSE_LBDOWN | PANEL_MOUSE_LBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_RIGHT) { + state = PANEL_MOUSE_RBDOWN | PANEL_MOUSE_RBPRESSED; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) { + state = PANEL_MOUSE_LBUP; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_RIGHT) { + state = PANEL_MOUSE_RBUP; + } + if (state & PANEL_MOUSE_DOWN) { // locate mouse event idx_mfocus = -1; diff --git a/Src/Orbiter/VCockpit.h b/Src/Orbiter/VCockpit.h index 8c70eea4a..e23c39d96 100644 --- a/Src/Orbiter/VCockpit.h +++ b/Src/Orbiter/VCockpit.h @@ -63,7 +63,7 @@ class VirtualCockpit { bool SetClickZone_Spherical (int i, const Vector &cnt, double rad); bool SetClickZone_Quadrilateral (int i, const Vector &p1, const Vector &p2, const Vector &p3, const Vector &p4); - bool ProcessMouse (UINT event, DWORD state, int x, int y); + bool ProcessMouse (const SDL_Event &event, int x, int y); void GetMouseState (int &idx, int &state, Vector &xs) const; inline void SetMouseState (int state) { mstate = state; } diff --git a/Src/Orbiter/VectorMap.cpp b/Src/Orbiter/VectorMap.cpp index 257345325..9341246af 100644 --- a/Src/Orbiter/VectorMap.cpp +++ b/Src/Orbiter/VectorMap.cpp @@ -1,16 +1,16 @@ // Copyright (c) Martin Schweiger // Licensed under the MIT License -#ifdef OAPIFUNC -#undef OAPIFUNC -#endif #include "DrawAPI.h" #include "VectorMap.h" #include "Psys.h" -#include "MFD.h" +#include "Mfd.h" #include "Util.h" +#include +#include + #define OUTLINE_COAST 1 #define OUTLINE_CONTOUR 2 #define OUTLINE_HORIZON 3 @@ -24,17 +24,11 @@ using namespace std; extern PlanetarySystem *g_psys; extern Vessel *g_focusobj; extern TimeData td; -extern char DBG_MSG[256]; bool VectorMap::bsetup = true; double VectorMap::cosp[NVTX_CIRCLE] = {0}; double VectorMap::sinp[NVTX_CIRCLE] = {0}; -//static COLORREF ObjectCol[3] = { -// Instrument::draw[0][0].col, -// Instrument::draw[1][0].col, -// Instrument::draw[2][0].col -//}; static COLORREF labelcol[6] = {0x00FFFF, 0xFFFF00, 0x4040FF, 0xFF00FF, 0x40FF40, 0xFF8080}; // ======================================================================= @@ -55,24 +49,9 @@ VectorMap::VectorMap (const Planet *pl) VectorMap::~VectorMap () { -#ifdef ASYNC_DRAWMAP - WaitThread (true); - WaitForSingleObject (hCommMutex, INFINITE); - threaddata.taskid = TASKID_TERMINATE; - ReleaseMutex (hCommMutex); - SetEvent (hActivateThread); - DWORD res = WaitForSingleObject (hRedrawThread, 100); // wait for thread termination - if (res == WAIT_TIMEOUT) - TerminateThread (hRedrawThread, 0); // force termination - - CloseHandle (hRedrawThread); - CloseHandle (hActivateThread); - CloseHandle (hCommMutex); -#endif + if(hMap) + oapiDestroySurface(hMap); - if (hBmpDraw) DeleteObject (hBmpDraw); - if (hDCmem) DeleteDC (hDCmem); - CloseGDIResources(); } // ======================================================================= @@ -94,7 +73,7 @@ void VectorMap::SetDefaults () latmax = latc+dlat; centermode = 0; dispflag = DISP_GRIDLINE | DISP_COASTLINE | DISP_CONTOURS | DISP_BASE | DISP_VESSEL | DISP_ORBITFOCUS | DISP_ORBITSEL | DISP_ORBITPLANE; - findflag = DISP_BASE | DISP_NAVAID | DISP_CUSTOM1 | DISP_VESSEL | DISP_MOON; + findflag = DISP_BASE | DISP_NAVAID | DISP_CUSTOMMARKER | DISP_VESSEL | DISP_MOON; drawdata.focus_disp = drawdata.tgtv_disp = drawdata.tgtb_disp = drawdata.sun_disp = false; selection.obj = NULL; selection.type = 0; @@ -108,19 +87,8 @@ void VectorMap::SetDefaults () bsetup = false; } - hDCmem = NULL; - hBmpDraw = NULL; + hMap = NULL; InitGDIResources(); - -#ifdef ASYNC_DRAWMAP - // Initialise the redraw thread - threaddata.taskid = 0; - DWORD id; - hActivateThread = CreateEvent (NULL, FALSE, TRUE, "MapEvent"); - hCommMutex = CreateMutex (NULL, FALSE, "MapCommMutex"); - hRedrawThread = CreateThread (NULL, 2048, Redraw_ThreadProc, this, 0, &id); - SetThreadPriority (hRedrawThread, THREAD_PRIORITY_IDLE); -#endif } // ======================================================================= @@ -134,26 +102,26 @@ void VectorMap::InitGDIResources () int screenh = GetSystemMetrics(SM_CYSCREEN); labelsize = screenh / 100; SetLabelSize(labelsize); - - penGridline = CreatePen (PS_SOLID, 1, 0x505050); - penCoast = CreatePen (PS_SOLID, 1, Instrument::draw[4][0].col); - penContour = CreatePen (PS_SOLID, 1, 0x0070C0); - penTerminator = CreatePen (PS_SOLID, 1, 0xC0C0C0); + + penGridline = oapiCreatePen(1, 1, 0x505050); + penCoast = oapiCreatePen (1, 1, Instrument::draw[4][0].col); + penContour = oapiCreatePen (1, 1, 0x0070C0); + penTerminator = oapiCreatePen (1, 1, 0xC0C0C0); int idx[3] = {0,1,3}; for (i = 0; i < 3; i++) { - penOrbitFuture[i] = CreatePen (PS_SOLID, 1, Instrument::draw[idx[i]][0].col); - penOrbitPast[i] = CreatePen (PS_SOLID, 1, Instrument::draw[idx[i]][1].col); - } - penFocusHorizon = CreatePen (PS_SOLID, 1, Instrument::draw[0][1].col); - penTargetHorizon = CreatePen (PS_SOLID, 1, Instrument::draw[1][1].col); - penNavmkr = CreatePen (PS_SOLID, 1, col_navaid); - penBase = CreatePen (PS_SOLID, 1, Instrument::draw[2][0].col); - penSelection = CreatePen (PS_SOLID, 1, Instrument::draw[1][0].col); + penOrbitFuture[i] = oapiCreatePen (1, 1, Instrument::draw[idx[i]][0].col); + penOrbitPast[i] = oapiCreatePen (1, 1, Instrument::draw[idx[i]][1].col); + } + penFocusHorizon = oapiCreatePen (1, 1, Instrument::draw[0][1].col); + penTargetHorizon = oapiCreatePen (1, 1, Instrument::draw[1][1].col); + penNavmkr = oapiCreatePen (1, 1, col_navaid); + penBase = oapiCreatePen (1, 1, Instrument::draw[2][0].col); + penSelection = oapiCreatePen (1, 1, Instrument::draw[1][0].col); for (i = 0; i < 3; i++) - penMarker[i] = CreatePen (PS_SOLID, 3, Instrument::draw[idx[i]][0].col); + penMarker[i] = oapiCreatePen (1, 3, Instrument::draw[idx[i]][0].col); nCustomMkr = 0; - LOGBRUSH lb = {BS_SOLID, 0x303030, 0}; - brushDay = CreateBrushIndirect (&lb); +// LOGBRUSH lb = {BS_SOLID, 0x303030, 0}; + brushDay = oapiCreateBrush(0x303030);//CreateBrushIndirect (&lb); } // ======================================================================= @@ -162,12 +130,12 @@ void VectorMap::SetLabelSize(int size) { if (fontLabel) { if (size == labelsize) return; // nothing to do - else DeleteObject(fontLabel); + else oapiReleaseFont(fontLabel); } labelsize = size; - fontLabel = CreateFont(-labelsize, 0, 0, 0, 400, 0, 0, 0, 0, 3, 2, 1, 49, "Arial"); -} + fontLabel = oapiCreateFont(-labelsize, true, "Arial", FONT_NORMAL); +} // ======================================================================= void VectorMap::AllocCustomResources () @@ -176,7 +144,7 @@ void VectorMap::AllocCustomResources () if (nCustomMkr) { for (i = 0; i < nCustomMkr; i++) - DeleteObject (penCustomMkr[i]); + oapiReleasePen (penCustomMkr[i]); delete []penCustomMkr; penCustomMkr = NULL; delete []colCustomMkr; @@ -184,11 +152,11 @@ void VectorMap::AllocCustomResources () } nCustomMkr = mkrset.nset; if (nCustomMkr) { - penCustomMkr = new HPEN[nCustomMkr]; + penCustomMkr = new oapi::Pen *[nCustomMkr]; colCustomMkr = new COLORREF[nCustomMkr]; for (i = 0; i < nCustomMkr; i++) { colCustomMkr[i] = labelcol[mkrset.set[i].list->colour]; - penCustomMkr[i] = CreatePen (PS_SOLID, 1, colCustomMkr[i]); + penCustomMkr[i] = oapiCreatePen (1, 1, colCustomMkr[i]); } } } @@ -198,30 +166,30 @@ void VectorMap::AllocCustomResources () void VectorMap::CloseGDIResources () { DWORD i; - DeleteObject (fontLabel); - DeleteObject (penGridline); - DeleteObject (penCoast); - DeleteObject (penContour); - DeleteObject (penTerminator); + oapiReleaseFont (fontLabel); + oapiReleasePen (penGridline); + oapiReleasePen (penCoast); + oapiReleasePen (penContour); + oapiReleasePen (penTerminator); //DeleteObject (penFocusGroundtrackFuture); //DeleteObject (penFocusGroundtrackPast); - DeleteObject (penFocusHorizon); + oapiReleasePen (penFocusHorizon); //DeleteObject (penTargetGroundtrackFuture); //DeleteObject (penTargetGroundtrackPast); - DeleteObject (penTargetHorizon); - DeleteObject (penNavmkr); - DeleteObject (penBase); - DeleteObject (penSelection); - DeleteObject (brushDay); + oapiReleasePen (penTargetHorizon); + oapiReleasePen (penNavmkr); + oapiReleasePen (penBase); + oapiReleasePen (penSelection); + oapiReleaseBrush (brushDay); for (i = 0; i < 3; i++) { - DeleteObject(penOrbitFuture[i]); - DeleteObject(penOrbitPast[i]); + oapiReleasePen(penOrbitFuture[i]); + oapiReleasePen(penOrbitPast[i]); } for (i = 0; i < 3; i++) - DeleteObject (penMarker[i]); + oapiReleasePen (penMarker[i]); if (nCustomMkr) { for (i = 0; i < nCustomMkr; i++) - DeleteObject (penCustomMkr[i]); + oapiReleasePen (penCustomMkr[i]); delete []penCustomMkr; penCustomMkr = NULL; delete []colCustomMkr; @@ -234,12 +202,6 @@ void VectorMap::CloseGDIResources () void VectorMap::Update () { -#ifdef ASYNC_DRAWMAP - if (ThreadBusy()) return; - // Don't update data if drawing in progress - // Should we rather wait here until the thread is free again? -#endif - double lng, lat, rad; if (!cbody) return; @@ -291,9 +253,6 @@ void VectorMap::Update () #ifdef UNDEF void VectorMap::UpdateGroundtrack () { -#ifdef ASYNC_DRAWMAP - if (ThreadBusy()) return; -#endif if (drawdata.focus_disp) gt_this.Update(); if (selection.type == DISP_VESSEL) gt_tgt.Update(); @@ -334,33 +293,18 @@ bool VectorMap::SetCBody (const CelestialBody *body) void VectorMap::SetCanvas (void *device_context, int w, int h) { -#ifdef ASYNC_DRAWMAP - WaitThread (true); - // make sure the drawing thread is not busy before replacing the bitmap -#endif - cw = w; ch = h; cntx = w/2; cnty = h/2; SetZoom (zoom); - // Create the drawing bitmap - HDC hDCtgt = GetDC (NULL); - if (hDCmem) { - DeleteDC (hDCmem); // delete current memory DC - } - if (hBmpDraw) DeleteObject (hBmpDraw); // delete drawing bitmap - if (hDCtgt) { - hDCmem = CreateCompatibleDC (hDCtgt); - hBmpDraw = CreateCompatibleBitmap (hDCtgt, w, h); // create new drawing bitmap - SetBkMode (hDCmem, TRANSPARENT); - SelectObject (hDCmem, GetStockObject (NULL_BRUSH)); - } else { - hDCmem = NULL; - hBmpDraw = NULL; + if(hMap != NULL) { + oapiDestroySurface(hMap); } - ReleaseDC (NULL, hDCtgt); + + hMap = oapiCreateSurface(w,h); + } // ======================================================================= @@ -511,7 +455,7 @@ bool VectorMap::GetObjPos (const OBJTYPE &obj, double &lng, double &lat) const Nav_VOR *vor = (const Nav_VOR*)obj.obj; vor->GetEquPos (lng, lat); } return true; - case DISP_CUSTOM1: { + case DISP_CUSTOMMARKER: { const VPoint *vp = (const VPoint*)obj.obj; lng = vp->lng; lat = vp->lat; @@ -594,7 +538,7 @@ const VectorMap::OBJTYPE VectorMap::FindObject (int x, int y) const } } - if (dispflag & DISP_CUSTOM1 && findflag & DISP_CUSTOM1) { + if (dispflag & DISP_CUSTOMMARKER && findflag & DISP_CUSTOMMARKER) { for (i = 0; i < mkrset.nset; i++) { if (mkrset.set[i].active) { const CustomMkrSpec *set = mkrset.set+i; @@ -606,7 +550,7 @@ const VectorMap::OBJTYPE VectorMap::FindObject (int x, int y) const if (dst2 < dst2min) { dst2min = dst2; obj.obj = set->vtx+j; - obj.type = DISP_CUSTOM1; + obj.type = DISP_CUSTOMMARKER; } } } @@ -657,32 +601,15 @@ const VectorMap::OBJTYPE VectorMap::FindObject (int x, int y) const // ======================================================================= -HBITMAP VectorMap::GetMap () +SURFHANDLE VectorMap::GetMap () { -#ifdef ASYNC_DRAWMAP - //WaitThread(); // wait for thread to finish drawing -#endif - return hBmpDraw; -} - -// ======================================================================= - -HDC VectorMap::GetDeviceContext () -{ -#ifdef ASYNC_DRAWMAP - WaitThread(); // wait for thread to finish drawing -#endif - return hDCmem; + return hMap; } // ======================================================================= void VectorMap::DrawMap () { -#ifdef ASYNC_DRAWMAP - if (ThreadBusy()) return; -#endif - DrawMap_engine(); } @@ -691,97 +618,94 @@ void VectorMap::DrawMap () void VectorMap::DrawMap_engine () { // called either directly (by DrawMap) or from the drawing thread - - tic(); - - // clear the bitmap - HBITMAP pBmp = (HBITMAP)SelectObject (hDCmem, hBmpDraw); - BitBlt (hDCmem, 0, 0, cw, ch, NULL, 0, 0, BLACKNESS); - HFONT pFont = (HFONT)SelectObject (hDCmem, fontLabel); - - // terminator line - if (planet && dispflag & DISP_TERMINATOR) { - switch (dispflag & DISP_TERMINATOR) { - case DISP_TERMINATOR_LINE: - DrawTerminatorLine (drawdata.sunlng, drawdata.sunlat); - break; - case DISP_TERMINATOR_SHADE: - DrawSunnySide (drawdata.sunlng, drawdata.sunlat, false); - break; - case DISP_TERMINATOR_BOTH: - DrawSunnySide (drawdata.sunlng, drawdata.sunlat, true); - break; + oapiClearSurface(hMap); + oapi::Sketchpad *skp = oapiGetSketchpad(hMap); + if(skp) { + skp->SetFont(fontLabel); + + // terminator line + if (planet && dispflag & DISP_TERMINATOR) { + switch (dispflag & DISP_TERMINATOR) { + case DISP_TERMINATOR_LINE: + DrawTerminatorLine (skp, drawdata.sunlng, drawdata.sunlat); + break; + case DISP_TERMINATOR_SHADE: + DrawSunnySide (skp, drawdata.sunlng, drawdata.sunlat, false); + break; + case DISP_TERMINATOR_BOTH: + DrawSunnySide (skp, drawdata.sunlng, drawdata.sunlat, true); + break; + } } - } - // grid lines - if (dispflag & DISP_GRIDLINE) - DrawGridlines (); + // grid lines + if (dispflag & DISP_GRIDLINE) + DrawGridlines (skp); - if (planet) { + if (planet) { - // coastlines - if (dispflag & DISP_COASTLINE) - DrawPolySet (&coast); + // coastlines + if (dispflag & DISP_COASTLINE) + DrawPolySet (skp, &coast); - // contour lines - if (dispflag & DISP_CONTOURS) - DrawPolySet (&contour); + // contour lines + if (dispflag & DISP_CONTOURS) + DrawPolySet (skp, &contour); - // navaids - if (dispflag & DISP_NAVAID) - DrawNavaids (); + // navaids + if (dispflag & DISP_NAVAID) + DrawNavaids (skp); - // custom marker sets - if (dispflag & DISP_CUSTOM1) { - for (DWORD i = 0; i < mkrset.nset; i++) { - if (mkrset.set[i].active) - DrawCustomMarkerSet (i); + // custom marker sets + if (dispflag & DISP_CUSTOMMARKER) { + for (DWORD i = 0; i < mkrset.nset; i++) { + if (mkrset.set[i].active) + DrawCustomMarkerSet (skp, i); + } } - } - // base markers - if (dispflag & DISP_BASE) - DrawBases (); + // base markers + if (dispflag & DISP_BASE) + DrawBases (skp); - // target base - if (drawdata.tgtb_disp) { - DrawMarker (drawdata.tgtblng, drawdata.tgtblat, drawdata.basename, 2); - } + // target base + if (drawdata.tgtb_disp) { + DrawMarker (skp, drawdata.tgtblng, drawdata.tgtblat, drawdata.basename, 2); + } - // natural satellites - if (dispflag & DISP_MOON || selection.type == DISP_MOON) - DrawMoons (); + // natural satellites + if (dispflag & DISP_MOON || selection.type == DISP_MOON) + DrawMoons (skp); - // vessels - if (dispflag & DISP_VESSEL) - DrawVessels (); - } + // vessels + if (dispflag & DISP_VESSEL) + DrawVessels (skp); + } - // target orbiter - if (drawdata.tgtv_disp) { - if (dispflag & DISP_HORIZONLINE) - DrawHorizon (drawdata.tgtvlng, drawdata.tgtvlat, drawdata.tgtvrad, false); - if (dispflag & DISP_GROUNDTRACK) - DrawGroundtrack (gt_tgt, 1); - else if (dispflag & DISP_ORBITPLANE) - DrawOrbitPlane (&drawdata.tgtvel, 1); - DrawMarker (drawdata.tgtvlng, drawdata.tgtvlat, drawdata.tgtname, 1); - } + // target orbiter + if (drawdata.tgtv_disp) { + if (dispflag & DISP_HORIZONLINE) + DrawHorizon (skp, drawdata.tgtvlng, drawdata.tgtvlat, drawdata.tgtvrad, false); + if (dispflag & DISP_GROUNDTRACK) + DrawGroundtrack (skp, gt_tgt, 1); + else if (dispflag & DISP_ORBITPLANE) + DrawOrbitPlane (skp, &drawdata.tgtvel, 1); + DrawMarker (skp, drawdata.tgtvlng, drawdata.tgtvlat, drawdata.tgtname, 1); + } - // selection marker - if (selection.type) - DrawSelectionMarker (selection); + // selection marker + if (selection.type) + DrawSelectionMarker (skp, selection); - SelectObject (hDCmem, pFont); - SelectObject (hDCmem, pBmp); + oapiReleaseSketchpad (skp); + } } // ======================================================================= -void VectorMap::DrawGridlines () +void VectorMap::DrawGridlines (oapi::Sketchpad *skp) { - HPEN ppen = (HPEN)SelectObject (hDCmem, penGridline); + oapi::Pen *ppen = skp->SetPen(penGridline); const double step = 30.0/DEG; const double eps = 1e-8; double lat0 = max(latmin,-Pi05); @@ -794,49 +718,50 @@ void VectorMap::DrawGridlines () double lat = ceil(lat0/step) * step; while (lat < lat1+eps) { y = mapy(lat); - MoveToEx (hDCmem, x0, y, NULL); - LineTo (hDCmem, x1, y); + skp->MoveTo (x0, y); + skp->LineTo (x1, y); lat += step; } double lng = ceil(lng0/step) * step; while (lng < lng1) { x = mapx(lng); - MoveToEx (hDCmem, x, y0, NULL); - LineTo (hDCmem, x, y1); + skp->MoveTo (x, y0); + skp->LineTo (x, y1); lng += step; } + + if(ppen) skp->SetPen(ppen); } // ======================================================================= -void VectorMap::DrawPolySet (const PolyLineSet *pls) +void VectorMap::DrawPolySet (oapi::Sketchpad *skp, const PolyLineSet *pls) { int j, n; int mapw = (int)(cw*PI/dlng); VPoint *v0; - HGDIOBJ ppen = NULL; - + oapi::Pen *ppen = NULL; switch (pls->type) { case OUTLINE_COAST: - ppen = SelectObject (hDCmem, penCoast); + ppen = skp->SetPen(penCoast); break; case OUTLINE_CONTOUR: - ppen = SelectObject (hDCmem, penContour); + ppen = skp->SetPen(penContour); break; } for (j = 0; j < pls->npoly; j++) { v0 = pls->vtx + pls->poly[j].vofs; n = (pls->poly[j].close ? pls->poly[j].nvtx : pls->poly[j].nvtx-1); - DrawPolyline (0, v0, n); + DrawPolyline (skp, 0, v0, n); } - if (ppen) SelectObject (hDCmem, ppen); + if(ppen) skp->SetPen(ppen); } // ======================================================================= -void VectorMap::DrawPolyline (int type, VPoint *vp, int n, bool close) +void VectorMap::DrawPolyline (oapi::Sketchpad *skp, int type, VPoint *vp, int n, bool close) { int i, x0, x1, y0, y1; int mapw = (int)(cw*PI/dlng); @@ -863,14 +788,14 @@ void VectorMap::DrawPolyline (int type, VPoint *vp, int n, bool close) y0 = mapy (va->lat); y1 = mapy (vb->lat); if ((y0 < 0 && y1 < 0) || (y0 >= ch && y1 >= ch)) continue; - MoveToEx (hDCmem, x0, y0, NULL); - LineTo (hDCmem, x1, y1); + skp->MoveTo ( x0, y0); + skp->LineTo ( x1, y1); } } // ======================================================================= -void VectorMap::DrawGroundtrackLine (int type, VPointGT *vp, int n, int n0, int n1) +void VectorMap::DrawGroundtrackLine (oapi::Sketchpad *skp, int type, VPointGT *vp, int n, int n0, int n1) { int i, x0, x1, y0, y1; int mapw = (int)(cw*PI/dlng); @@ -904,40 +829,39 @@ void VectorMap::DrawGroundtrackLine (int type, VPointGT *vp, int n, int n0, int double scl = (1.0-va->rad)/(vb->rad-va->rad); x0 += (int)((x1-x0)*scl); y0 += (int)((y1-y0)*scl); - Rectangle (hDCmem, x0-2, y0-2, x0+3, y0+3); + skp->Rectangle (x0-2, y0-2, x0+3, y0+3); if (replicate) - Rectangle (hDCmem, x0-2-mapw, y0-2, x0+3-mapw, y0+3); + skp->Rectangle (x0-2-mapw, y0-2, x0+3-mapw, y0+3); } else if (vb->rad < 1.0) { double scl = (1.0-vb->rad)/(vb->rad-va->rad); x1 += (int)((x1-x0)*scl); y1 += (int)((y1-y0)*scl); - Rectangle (hDCmem, x1-2, y1-2, x1+3, y1+3); + skp->Rectangle (x1-2, y1-2, x1+3, y1+3); if (replicate) - Rectangle (hDCmem, x1-2-mapw, y1-2, x1+3-mapw, y1+3); + skp->Rectangle (x1-2-mapw, y1-2, x1+3-mapw, y1+3); } if (replicate && x0 != x1) { int xm = (cw-mapw)/2; if (x0 > cntx) xm += mapw; int ym = y0 + ((xm-x0)*(y1-y0))/(x1-x0); - MoveToEx (hDCmem, x0, y0, NULL); - LineTo (hDCmem, xm, ym); + skp->MoveTo (x0, y0); + skp->LineTo (xm, ym); int dx = (x0 > cntx ? -mapw:mapw); - MoveToEx (hDCmem, xm+dx, ym, NULL); - LineTo (hDCmem, x1+dx, y1); + skp->MoveTo (xm+dx, ym); + skp->LineTo (x1+dx, y1); } else { - MoveToEx (hDCmem, x0, y0, NULL); - LineTo (hDCmem, x1, y1); + skp->MoveTo (x0, y0); + skp->LineTo (x1, y1); } } } // ======================================================================= -void VectorMap::DrawNavaids () +void VectorMap::DrawNavaids (oapi::Sketchpad *skp) { - SetTextColor (hDCmem, col_navaid); - HPEN ppen = (HPEN)SelectObject (hDCmem, penNavmkr); - SelectObject (hDCmem, GetStockObject (NULL_BRUSH)); + skp->SetTextColor (col_navaid); + oapi::Pen *ppen = skp->SetPen (penNavmkr); if (planet && planet->nNav()) { static char cbuf[32]; @@ -953,12 +877,12 @@ void VectorMap::DrawNavaids () vor->GetEquPos (lng, lat); if (GetMapPos (lng, lat, x, y)) { if (drawdot) { - SetPixel (hDCmem, x, y, col_navaid); + skp->Pixel (x, y, col_navaid); } else { - Ellipse (hDCmem, x-2, y-2, x+3, y+3); + skp->Ellipse (x-2, y-2, x+3, y+3); if (drawlabel) { sprintf (cbuf, "%s %0.2f", vor->GetId(), vor->GetFreq()); - TextOut (hDCmem, x+3, y, cbuf, strlen(cbuf)); + skp->Text (x+3, y, cbuf, strlen(cbuf)); } } } @@ -967,31 +891,31 @@ void VectorMap::DrawNavaids () } } - SelectObject (hDCmem, ppen); + if(ppen) skp->SetPen (ppen); } // ======================================================================= -void VectorMap::DrawVesselOrbit (Vessel *v) +void VectorMap::DrawVesselOrbit (oapi::Sketchpad *skp, Vessel *v) { bool isfocus = (v == g_focusobj); if (dispflag & DISP_HORIZONLINE) { const SurfParam *sp = v->GetSurfParam(); if (sp && sp->ref == cbody) { - DrawHorizon (sp->lng, sp->lat, sp->rad/cbody->Size(), isfocus); + DrawHorizon (skp, sp->lng, sp->lat, sp->rad/cbody->Size(), isfocus); } } if (v->ElRef() == cbody) { if (dispflag & DISP_GROUNDTRACK) - DrawGroundtrack (isfocus ? gt_this : gt_tgt, isfocus ? 0:1); + DrawGroundtrack (skp, isfocus ? gt_this : gt_tgt, isfocus ? 0:1); if (dispflag & DISP_ORBITPLANE) - DrawOrbitPlane (v->Els(), isfocus ? 0:1); + DrawOrbitPlane (skp, v->Els(), isfocus ? 0:1); } } // ======================================================================= -void VectorMap::DrawVessels () +void VectorMap::DrawVessels (oapi::Sketchpad *skp) { Vessel *v; bool focus_drawn = false; @@ -999,12 +923,12 @@ void VectorMap::DrawVessels () if (dispflag & DISP_ORBITSEL && selection.type == DISP_VESSEL) { v = (Vessel*)selection.obj; if (v == g_focusobj || !(dispflag & DISP_FOCUSONLY)) { - DrawVesselOrbit (v); + DrawVesselOrbit (skp, v); focus_drawn = (v == g_focusobj); } } if (dispflag & DISP_ORBITFOCUS && !focus_drawn) - DrawVesselOrbit (g_focusobj); + DrawVesselOrbit (skp, g_focusobj); for (DWORD i = 0; i < g_psys->nVessel(); i++) { v = g_psys->GetVessel(i); @@ -1012,14 +936,14 @@ void VectorMap::DrawVessels () continue; const SurfParam *sp = v->GetSurfParam(); if (sp && sp->ref == planet) { - DrawMarker (sp->lng, sp->lat, v->Name(), v == g_focusobj ? 0:1); + DrawMarker (skp, sp->lng, sp->lat, v->Name(), v == g_focusobj ? 0:1); } } } // ======================================================================= -void VectorMap::DrawMoons () +void VectorMap::DrawMoons (oapi::Sketchpad *skp) { const CelestialBody *moon; double lng, lat, rad; @@ -1028,9 +952,9 @@ void VectorMap::DrawMoons () moon = (const CelestialBody*)selection.obj; if (moon->ElRef() == cbody) { if (dispflag & DISP_GROUNDTRACK) { - DrawGroundtrack (gt_tgt, 2); + DrawGroundtrack (skp, gt_tgt, 2); } if (dispflag & DISP_ORBITPLANE) { - DrawOrbitPlane (moon->Els(), 2); + DrawOrbitPlane (skp, moon->Els(), 2); } } } @@ -1039,17 +963,17 @@ void VectorMap::DrawMoons () for (DWORD i = 0; i < cbody->nSecondary(); i++) { moon = cbody->Secondary (i); cbody->GlobalToEquatorial (moon->GPos(), lng, lat, rad); - DrawMarker (lng, lat, moon->Name(), 2); + DrawMarker (skp, lng, lat, moon->Name(), 2); } } // ======================================================================= -void VectorMap::DrawBases () +void VectorMap::DrawBases (oapi::Sketchpad *skp) { bool drawlabel = (mapx_scale > 700); - if (drawlabel) SetTextColor (hDCmem, Instrument::draw[2][0].col); - HPEN ppen = (HPEN)SelectObject (hDCmem, penBase); + if (drawlabel) skp->SetTextColor (Instrument::draw[2][0].col); + oapi::Pen *ppen = skp->SetPen (penBase); if (planet && g_psys->nBase (planet)) { int x, y; @@ -1058,21 +982,21 @@ void VectorMap::DrawBases () Base *base = g_psys->GetBase (planet, i); base->EquPos (lng, lat); if (GetMapPos (lng, lat, x, y)) { - Rectangle (hDCmem, x-3, y-3, x+4, y+4); + skp->Rectangle (x-3, y-3, x+4, y+4); if (drawlabel) - TextOut (hDCmem, x+3, y, base->Name(), strlen(base->Name())); + skp->Text (x+3, y, base->Name(), strlen(base->Name())); } } } - SelectObject (hDCmem, ppen); + if(ppen) skp->SetPen (ppen); } // ======================================================================= -void VectorMap::DrawCustomMarkerSet (int idx) +void VectorMap::DrawCustomMarkerSet (oapi::Sketchpad *skp, int idx) { - HPEN ppen = NULL; + oapi::Pen *ppen = NULL; int i, x, y; const char *label; @@ -1082,79 +1006,79 @@ void VectorMap::DrawCustomMarkerSet (int idx) bool drawdot = (mapx_scale < 400); bool drawlabel = (mapx_scale > 1400); - if (drawlabel) SetTextColor (hDCmem, colCustomMkr[idx]); - if (!drawdot) ppen = (HPEN)SelectObject (hDCmem, penCustomMkr[idx]); + if (drawlabel) skp->SetTextColor (colCustomMkr[idx]); + if (!drawdot) ppen = skp->SetPen (penCustomMkr[idx]); for (i = 0; i < set->nvtx; i++) { if (GetMapPos (set->vtx[i].lng, set->vtx[i].lat, x, y)) { if (drawdot) { - SetPixel (hDCmem, x, y, colCustomMkr[idx]); + skp->Pixel (x, y, colCustomMkr[idx]); } else { - Ellipse (hDCmem, x-2, y-2, x+3, y+3); + skp->Ellipse (x-2, y-2, x+3, y+3); if (drawlabel && set->list->marker[i].label[0].size()) { WCHAR wlabel[256]; MultiByteToWideChar(CP_UTF8, 0, set->list->marker[i].label[0].c_str(), -1, wlabel, 256); - TextOutW (hDCmem, x+3, y, wlabel, wcslen(wlabel)); + skp->TextW (x+3, y, wlabel, wcslen(wlabel)); } } } } - if (ppen) SelectObject (hDCmem, ppen); + if (ppen) skp->SetPen (ppen); } // ======================================================================= -void VectorMap::DrawMarker (double lng, double lat, const char *name, int which) +void VectorMap::DrawMarker (oapi::Sketchpad *skp, double lng, double lat, const char *name, int which) { int x, y; if (!GetMapPos (lng, lat, x, y)) return; // position not on map - HPEN ppen = (HPEN)SelectObject (hDCmem, penMarker[which]); - MoveToEx (hDCmem, x-10, y, NULL); - LineTo (hDCmem, x+11, y); - MoveToEx (hDCmem, x, y-10, NULL); - LineTo (hDCmem, x, y+11); - SelectObject (hDCmem, ppen); - SetTextColor (hDCmem, Instrument::draw[which][0].col); - TextOut (hDCmem, x+3, which==2 ? y:y-labelsize-3, name, min((size_t)64,strlen(name))); + oapi::Pen *ppen = skp->SetPen(penMarker[which]); + skp->MoveTo (x-10, y); + skp->LineTo (x+11, y); + skp->MoveTo (x, y-10); + skp->LineTo (x, y+11); + if (ppen) skp->SetPen (ppen); + skp->SetTextColor (Instrument::draw[which][0].col); + skp->Text (x+3, which==2 ? y:y-labelsize-3, name, min(64,(int)strlen(name))); } // ======================================================================= -void VectorMap::DrawSelectionMarker (const OBJTYPE obj) +void VectorMap::DrawSelectionMarker (oapi::Sketchpad *skp, const OBJTYPE obj) { double lng, lat; int x, y; if (GetObjPos (obj, lng, lat)) if (GetMapPos (lng, lat, x, y)) { - HPEN ppen = (HPEN)SelectObject (hDCmem, penSelection); - Ellipse (hDCmem, x-5, y-5, x+6, y+6); - Ellipse (hDCmem, x-8, y-8, x+9, y+9); - SelectObject (hDCmem, ppen); + oapi::Pen *ppen = skp->SetPen(penSelection); + skp->Ellipse (x-5, y-5, x+6, y+6); + skp->Ellipse (x-8, y-8, x+9, y+9); + if (ppen) skp->SetPen (ppen); } } // ======================================================================= -void VectorMap::DrawTerminatorLine (double sunlng, double sunlat) +void VectorMap::DrawTerminatorLine (oapi::Sketchpad *skp, double sunlng, double sunlat) { - HPEN ppen = (HPEN)SelectObject (hDCmem, penTerminator); + oapi::Pen *ppen = skp->SetPen(penTerminator); VPoint *p = GreatCircle (sunlng, sunlat); - DrawPolyline (0, p, NVTX_CIRCLE); - SelectObject (hDCmem, ppen); + DrawPolyline (skp, 0, p, NVTX_CIRCLE); + if (ppen) skp->SetPen (ppen); } // ======================================================================= -void VectorMap::DrawSunnySide (double sunlng, double sunlat, bool terminator) +void VectorMap::DrawSunnySide (oapi::Sketchpad *skp, double sunlng, double sunlat, bool terminator) { int i, cut, ybase, idx0, idx1; int mapw = (int)(cw*PI/dlng); if (sunlat >= 0) ybase = max (0, mapy(Pi05)); else ybase = min (ch, mapy(-Pi05)); VPoint *p = GreatCircle (sunlng, sunlat); - POINT pt[NVTX_CIRCLE], ptt[NVTX_CIRCLE+4]; + oapi::IVECTOR2 pt[NVTX_CIRCLE+4], ptt[NVTX_CIRCLE+4]; for (i = 0; i < NVTX_CIRCLE; i++) { pt[i].x = mapx(p[i].lng); pt[i].y = mapy(p[i].lat); @@ -1176,7 +1100,7 @@ void VectorMap::DrawSunnySide (double sunlng, double sunlat, bool terminator) idx0 = 1; } else { for (idx0 = 2; idx0 < NVTX_CIRCLE+1; idx0++) { - if (ptt[idx0+1].x > 0) break; + if (ptt[idx0+1].x >= 0) break; } } if (ptt[NVTX_CIRCLE+1].x < cw) { @@ -1185,44 +1109,51 @@ void VectorMap::DrawSunnySide (double sunlng, double sunlat, bool terminator) idx1 = NVTX_CIRCLE+2; } else { for (idx1 = NVTX_CIRCLE+1; idx1 > 2; idx1--) { - if (ptt[idx1-1].x < cw) break; + if (ptt[idx1-1].x <= cw) break; } } if (idx1 <= idx0) return; ptt[idx0-1].x = ptt[idx0].x; - ptt[idx0-1].y = ybase; + ptt[idx0-1].y = 10000;//ybase - 10000; ptt[idx1+1].x = ptt[idx1].x; - ptt[idx1+1].y = ybase; + ptt[idx1+1].y = 10000;//ybase - 10000; + + oapi::Pen *ppen = NULL; + if(terminator) + ppen = skp->SetPen(penTerminator); + else { + //FIXME NULL_PEN + //ppen = skp->SetPen(penTerminator); + } + skp->SetBrush(brushDay); + skp->Polygon (ptt+(idx0-1), idx1-idx0+3); + if (ppen) skp->SetPen(ppen); - HPEN ppen = (HPEN)SelectObject (hDCmem, terminator ? penTerminator : GetStockObject (NULL_PEN)); - SelectObject (hDCmem, brushDay); - Polygon (hDCmem, ptt+(idx0-1), idx1-idx0+3); - SelectObject (hDCmem, ppen); - SelectObject (hDCmem, GetStockObject (NULL_BRUSH)); } // ======================================================================= -void VectorMap::DrawOrbitPlane (const Elements *el, int which) +void VectorMap::DrawOrbitPlane (oapi::Sketchpad *skp, const Elements *el, int which) { - HPEN ppen = (HPEN)SelectObject (hDCmem, penOrbitFuture[which]); + oapi::Pen *ppen = skp->SetPen(penOrbitFuture[which]); + static VPoint p[NVTX_CIRCLE]; CalcOrbitProj (el, cbody, p); - DrawPolyline (OUTLINE_ORBITPLANE, p, NVTX_CIRCLE); - SelectObject (hDCmem, ppen); + DrawPolyline (skp, OUTLINE_ORBITPLANE, p, NVTX_CIRCLE); + if (ppen) skp->SetPen(ppen); } // ======================================================================= -void VectorMap::DrawGroundtrack (Groundtrack >, int which) +void VectorMap::DrawGroundtrack (oapi::Sketchpad *skp, Groundtrack >, int which) { - DrawGroundtrack_past (gt, which); - DrawGroundtrack_future (gt, which); + DrawGroundtrack_past (skp, gt, which); + DrawGroundtrack_future (skp, gt, which); } -void VectorMap::DrawGroundtrack_past (Groundtrack >, int which) +void VectorMap::DrawGroundtrack_past (oapi::Sketchpad *skp, Groundtrack >, int which) { - HPEN ppen = (HPEN)SelectObject (hDCmem, penOrbitPast[which]); + oapi::Pen *ppen = skp->SetPen(penOrbitPast[which]); #ifdef UNDEF if (gt.vfirst <= gt.vcurr) { DrawGroundtrackLine (OUTLINE_GROUNDTRACK, gt.vtx+gt.vfirst, gt.vcurr-gt.vfirst+1); @@ -1233,13 +1164,13 @@ void VectorMap::DrawGroundtrack_past (Groundtrack >, int which) LineTo (hDCmem, mapx(gt.vtx[0].lng), mapy(gt.vtx[0].lat)); } #endif - DrawGroundtrackLine (OUTLINE_GROUNDTRACK, gt.vtx, gt.nvtx, gt.vfirst, gt.vcurr); - SelectObject (hDCmem, ppen); + DrawGroundtrackLine (skp, OUTLINE_GROUNDTRACK, gt.vtx, gt.nvtx, gt.vfirst, gt.vcurr); + if (ppen) skp->SetPen(ppen); } -void VectorMap::DrawGroundtrack_future (Groundtrack >, int which) +void VectorMap::DrawGroundtrack_future (oapi::Sketchpad *skp, Groundtrack >, int which) { - HPEN ppen = (HPEN)SelectObject (hDCmem, penOrbitFuture[which]); + oapi::Pen *ppen = skp->SetPen(penOrbitFuture[which]); #ifdef UNDEF if (gt.vcurr <= gt.vlast) { DrawGroundtrackLine (OUTLINE_GROUNDTRACK, gt.vtx+gt.vcurr, gt.vlast-gt.vcurr+1); @@ -1250,19 +1181,23 @@ void VectorMap::DrawGroundtrack_future (Groundtrack >, int which) LineTo (hDCmem, mapx(gt.vtx[0].lng), mapy(gt.vtx[0].lat)); } #endif - DrawGroundtrackLine (OUTLINE_GROUNDTRACK, gt.vtx, gt.nvtx, gt.vcurr, gt.vlast); - SelectObject (hDCmem, ppen); + DrawGroundtrackLine (skp, OUTLINE_GROUNDTRACK, gt.vtx, gt.nvtx, gt.vcurr, gt.vlast); + if (ppen) skp->SetPen(ppen); } // ======================================================================= -void VectorMap::DrawHorizon (double lng, double lat, double rad, bool focus) +void VectorMap::DrawHorizon (oapi::Sketchpad *skp, double lng, double lat, double rad, bool focus) { - HPEN ppen = (HPEN)SelectObject (hDCmem, focus ? penFocusHorizon:penTargetHorizon); + // If the vessel is below ground, we need to bail out to prevent NaN issues further down the line + if(rad < 1.0) { + return; + } + oapi::Pen *ppen = skp->SetPen(focus ? penFocusHorizon:penTargetHorizon); double dst = 1.0/rad; VPoint *vp = SmallCircle (lng, lat, dst); - DrawPolyline (OUTLINE_HORIZON, vp, NVTX_CIRCLE); - SelectObject (hDCmem, ppen); + DrawPolyline (skp, OUTLINE_HORIZON, vp, NVTX_CIRCLE); + if (ppen) skp->SetPen(ppen); } // ======================================================================= @@ -1346,77 +1281,6 @@ VPoint *VectorMap::SmallCircle (double lng, double lat, double dst) return vp; } - -// ======================================================================= -// Thread interface -// ======================================================================= -#ifdef ASYNC_DRAWMAP -// ======================================================================= - -void VectorMap::WaitThread (bool abortOp) -{ - if (!ThreadBusy()) return; - if (abortOp) { - WaitForSingleObject (hCommMutex, INFINITE); - threaddata.taskid = TASKID_ABORT; - ReleaseMutex (hCommMutex); - } - while (ThreadBusy()) { - Sleep(10); - } -} - -// ======================================================================= -// Sends a request for a redraw to the draw thread and returns immediately - -bool VectorMap::AsyncDrawMap () -{ - if (ThreadBusy()) return false; // redraw is already in progress - DWORD res = WaitForSingleObject (hCommMutex, 10); - if (res == WAIT_TIMEOUT) return false; // could not get mutex in time - threaddata.taskid = TASKID_DRAW; - ReleaseMutex (hCommMutex); - SetEvent (hActivateThread); - return true; -} - -// ======================================================================= - -void VectorMap::thEngine () -{ - const DWORD idle = 100; - DWORD flag; - bool keep_going = true; - while (keep_going) { - WaitForSingleObject (hActivateThread, INFINITE); // wait for task - WaitForSingleObject (hCommMutex, INFINITE); // secure comm data - flag = threaddata.taskid; - ReleaseMutex (hCommMutex); - switch (flag) { - case TASKID_TERMINATE: - keep_going = false; - break; - case TASKID_DRAW: - DrawMap_engine (); - break; - } - WaitForSingleObject (hCommMutex, INFINITE); - threaddata.taskid = 0; // signal task complete - ReleaseMutex (hCommMutex); - } -} - -// ======================================================================= - -DWORD WINAPI VectorMap::Redraw_ThreadProc (void *data) -{ - VectorMap *map = (VectorMap*)data; - map->thEngine(); - return 0; -} -#endif // ASYNC_DRAWMAP - - // ======================================================================= // ======================================================================= @@ -1681,4 +1545,4 @@ void Groundtrack::Update () vupdt = (vupdt+1) % nvtx; } omega_updt = fabs (omega_updt); -} \ No newline at end of file +} diff --git a/Src/Orbiter/VectorMap.h b/Src/Orbiter/VectorMap.h index d4bef4013..68db517fd 100644 --- a/Src/Orbiter/VectorMap.h +++ b/Src/Orbiter/VectorMap.h @@ -16,21 +16,21 @@ #define NVTX_CIRCLE 64 -#define DISP_GRIDLINE 0x0001 -#define DISP_COASTLINE 0x0002 -#define DISP_CONTOURS 0x0004 -#define DISP_VESSEL 0x0008 -#define DISP_FOCUSONLY 0x0010 -#define DISP_HORIZONLINE 0x0020 -#define DISP_NAVAID 0x0040 -#define DISP_BASE 0x0080 -#define DISP_MOON 0x0100 -#define DISP_CUSTOM1 0x0200 -#define DISP_ORBITPLANE 0x0400 -#define DISP_GROUNDTRACK 0x0800 -#define DISP_ORBITFOCUS 0x1000 -#define DISP_ORBITSEL 0x2000 -#define DISP_TERMINATOR 0xC000 +#define DISP_GRIDLINE 0x0001 +#define DISP_COASTLINE 0x0002 +#define DISP_CONTOURS 0x0004 +#define DISP_VESSEL 0x0008 +#define DISP_FOCUSONLY 0x0010 +#define DISP_HORIZONLINE 0x0020 +#define DISP_NAVAID 0x0040 +#define DISP_BASE 0x0080 +#define DISP_MOON 0x0100 +#define DISP_CUSTOMMARKER 0x0200 +#define DISP_ORBITPLANE 0x0400 +#define DISP_GROUNDTRACK 0x0800 +#define DISP_ORBITFOCUS 0x1000 +#define DISP_ORBITSEL 0x2000 +#define DISP_TERMINATOR 0xC000 #define DISP_TERMINATOR_NONE 0x0000 #define DISP_TERMINATOR_LINE 0x4000 @@ -138,10 +138,9 @@ class VectorMap { void SetFindFlags (DWORD flag) { findflag = flag; } DWORD GetFindFlags () const { return findflag; } - // Returns the drawing bitmap and HDC. + // Returns the drawing bitmap SURFHANDLE. // Note: waits for the drawing thread to finish - HBITMAP GetMap (); - HDC GetDeviceContext(); + SURFHANDLE GetMap (); void DrawMap (); // redraw directly @@ -200,28 +199,28 @@ class VectorMap { // drawing logical object sets void DrawMap_engine ();// redraw map - void DrawGridlines (); - void DrawPolySet (const PolyLineSet *pls); - void DrawPolyline (int type, VPoint *vp, int n, bool close = true); - void DrawNavaids (); - void DrawVessels (); - void DrawMoons (); - void DrawVesselOrbit (Vessel *v); - void DrawBases (); - void DrawCustomMarkerSet (int idx); - void DrawTerminatorLine (double sunlng, double sunlat); - void DrawSunnySide (double sunlng, double sunlat, bool terminator); - void DrawOrbitPlane (const Elements *el, int which); - void DrawGroundtrack (Groundtrack >, int which); - void DrawGroundtrack_past (Groundtrack >, int which); - void DrawGroundtrack_future (Groundtrack >, int which); - void DrawHorizon (double lng, double lat, double rad, bool focus); - void DrawGroundtrackLine (int type, VPointGT *vp, int n, int n0, int n1); + void DrawGridlines (oapi::Sketchpad *skp); + void DrawPolySet (oapi::Sketchpad *skp, const PolyLineSet *pls); + void DrawPolyline (oapi::Sketchpad *skp, int type, VPoint *vp, int n, bool close = true); + void DrawNavaids (oapi::Sketchpad *skp); + void DrawVessels (oapi::Sketchpad *skp); + void DrawMoons (oapi::Sketchpad *skp); + void DrawVesselOrbit (oapi::Sketchpad *skp, Vessel *v); + void DrawBases (oapi::Sketchpad *skp); + void DrawCustomMarkerSet (oapi::Sketchpad *skp, int idx); + void DrawTerminatorLine (oapi::Sketchpad *skp, double sunlng, double sunlat); + void DrawSunnySide (oapi::Sketchpad *skp, double sunlng, double sunlat, bool terminator); + void DrawOrbitPlane (oapi::Sketchpad *skp, const Elements *el, int which); + void DrawGroundtrack (oapi::Sketchpad *skp, Groundtrack >, int which); + void DrawGroundtrack_past (oapi::Sketchpad *skp, Groundtrack >, int which); + void DrawGroundtrack_future (oapi::Sketchpad *skp, Groundtrack >, int which); + void DrawHorizon (oapi::Sketchpad *skp, double lng, double lat, double rad, bool focus); + void DrawGroundtrackLine (oapi::Sketchpad *skp, int type, VPointGT *vp, int n, int n0, int n1); // drawing primitives - void DrawMarker (double lng, double lat, const char *name, int which); // which: 0=focusobj, 1=orbittarget, 2=basetarget + void DrawMarker (oapi::Sketchpad *skp, double lng, double lat, const char *name, int which); // which: 0=focusobj, 1=orbittarget, 2=basetarget - void DrawSelectionMarker (const OBJTYPE obj); + void DrawSelectionMarker (oapi::Sketchpad *skp, const OBJTYPE obj); // calculate the vertex points of a great circle on the sphere // The circle describes the intersection of the sphere with an @@ -272,67 +271,28 @@ class VectorMap { // ------------------------------------------------------------------ // Drawing resources - HDC hDCmem; // memory device context - HBITMAP hBmpDraw; // bitmap for background drawing - HPEN penGridline; - HPEN penCoast; - HPEN penContour; - HPEN penTerminator; - HPEN penOrbitFuture[3]; // 0=focus, 1=vessel, 2=moon - HPEN penOrbitPast[3]; // 0=focus, 1=vessel, 2=moon - HPEN penFocusHorizon; - HPEN penTargetHorizon; - HPEN penNavmkr; - HPEN penBase; - HPEN penSelection; - HPEN penMarker[3]; - HPEN *penCustomMkr; - HBRUSH brushDay; - HFONT fontLabel; + SURFHANDLE hMap; // bitmap for background drawing + oapi::Pen *penGridline; + oapi::Pen *penCoast; + oapi::Pen *penContour; + oapi::Pen *penTerminator; + oapi::Pen *penOrbitFuture[3]; // 0=focus, 1=vessel, 2=moon + oapi::Pen *penOrbitPast[3]; // 0=focus, 1=vessel, 2=moon + oapi::Pen *penFocusHorizon; + oapi::Pen *penTargetHorizon; + oapi::Pen *penNavmkr; + oapi::Pen *penBase; + oapi::Pen *penSelection; + oapi::Pen *penMarker[3]; + oapi::Pen **penCustomMkr; + oapi::Brush *brushDay; + oapi::Font *fontLabel; COLORREF *colCustomMkr; DWORD nCustomMkr; private: void InitGDIResources(); void CloseGDIResources(); - -#ifdef ASYNC_DRAWMAP - // ------------------------------------------------------------------ - // Thread interface for asynchronous drawing -protected: - HANDLE hRedrawThread; // redraw thread handle - HANDLE hActivateThread; // thread activation event handle; set by the main thread to start thread execution - HANDLE hCommMutex; // mutex for protecting communication between main and drawing threads - - // Blocking wait: returns after thread has completed its current operation (if any) - // abortOp: this flag asks the thread to terminate its current operation. Use this - // if the results of the current operation are obsolete and you want the function - // to return as quickly as possible - void WaitThread (bool abortOp = false); - - // Thread communication data. This structure should only be accessed - // after acquiring the hCommMutex. - struct THREADDATA { - int taskid; // task identifier - } threaddata; - -public: - // Returns true if thread is busy, false if waiting - // is it safe to do this without locking the mutex? - inline bool ThreadBusy () const - { return threaddata.taskid != 0; } - - // Request asynchronous map redraw - bool AsyncDrawMap (); - -protected: - // the following functions (th*) are accessed only by the thread - void thEngine (); // redraw thread loop - -private: - static DWORD WINAPI Redraw_ThreadProc (void*); // thread entry point - // ------------------------------------------------------------------ -#endif // ASYNC_DRAWMAP }; -#endif // !__VECTORMAP_H \ No newline at end of file +#endif // !__VECTORMAP_H diff --git a/Src/Orbiter/cmdline.cpp b/Src/Orbiter/cmdline.cpp index ff257af11..0c30d498a 100644 --- a/Src/Orbiter/cmdline.cpp +++ b/Src/Orbiter/cmdline.cpp @@ -2,15 +2,22 @@ // Licensed under the MIT License #include +#include #include #include "cmdline.h" #include "Orbiter.h" #include "Launchpad.h" -CommandLine::CommandLine(const PSTR cmdLine) +CommandLine::CommandLine(int argc, const char** cmdLine) { - m_cmdLine = (std::string(cmdLine ? cmdLine : "")); - ParseCmdLine(cmdLine); + auto ss = std::stringstream(); + for (int i = 1; i < argc; i++) { + if (i != 1) + ss << " "; + ss << (cmdLine[i] ? cmdLine[i] : ""); + } + m_cmdLine = ss.str(); + ParseCmdLine(); } const char* CommandLine::CmdLine() const @@ -28,9 +35,9 @@ bool CommandLine::GetOption(UINT id, const std::string** value) const return false; } -void CommandLine::ParseCmdLine(const PSTR cmdLine) +void CommandLine::ParseCmdLine() { - PSTR pc = cmdLine; + const char *pc = m_cmdLine.c_str(); bool groupKey = false; while (*pc) { @@ -42,7 +49,7 @@ void CommandLine::ParseCmdLine(const PSTR cmdLine) } } -bool CommandLine::ParseNextOption(PSTR& cmdLine, bool& groupKey, Option& option) +bool CommandLine::ParseNextOption(const char*& cmdLine, bool& groupKey, Option& option) { bool isLongKey; bool isQuotedVal; @@ -70,7 +77,7 @@ bool CommandLine::ParseNextOption(PSTR& cmdLine, bool& groupKey, Option& option) } const char* termKeyChar = (isLongKey ? " \t=" : " \t"); // '=' as key-value separator only allowed for long keys std::set termK(termKeyChar, termKeyChar + strlen(termKeyChar) + 1); // include '\0' in set - PSTR endKey = cmdLine; + const char* endKey = cmdLine; while (termK.find(*endKey) == termK.end()) endKey++; size_t keyLen = endKey - cmdLine; @@ -104,7 +111,7 @@ bool CommandLine::ParseNextOption(PSTR& cmdLine, bool& groupKey, Option& option) cmdLine++; // skip starting quotes const char* termValChar = (isQuotedParam || isQuotedVal ? "\"" : " \t"); // for quoted values, only accept quotes as terminator std::set termV(termValChar, termValChar + strlen(termValChar) + 1); // include '\0' in set - PSTR endVal = cmdLine; + const char* endVal = cmdLine; while (termV.find(*endVal) == termV.end()) endVal++; size_t valLen = endVal - cmdLine; @@ -148,11 +155,8 @@ void CommandLine::ApplyOptions() } } - - - -orbiter::CommandLine::CommandLine(Orbiter* pOrbiter, const PSTR cmdLine) - : ::CommandLine(cmdLine) +orbiter::CommandLine::CommandLine(Orbiter* pOrbiter, int argc, const char **cmdLine) + : ::CommandLine(argc, cmdLine) , m_pOrbiter(pOrbiter) { CFG_CMDLINEPRM& cfg = m_pOrbiter->Cfg()->CfgCmdlinePrm; @@ -259,8 +263,8 @@ void orbiter::CommandLine::PrintHelpAndExit() const exit(0); } -orbiter::CommandLine& orbiter::CommandLine::InstanceImpl(Orbiter* pOrbiter, const PSTR cmdLine) +orbiter::CommandLine& orbiter::CommandLine::InstanceImpl(Orbiter* pOrbiter, int argc, const char **cmdLine) { - static orbiter::CommandLine instance{ pOrbiter, cmdLine }; + static orbiter::CommandLine instance{ pOrbiter, argc, cmdLine }; return instance; } diff --git a/Src/Orbiter/cmdline.h b/Src/Orbiter/cmdline.h index fefc303d2..7cb46d63b 100644 --- a/Src/Orbiter/cmdline.h +++ b/Src/Orbiter/cmdline.h @@ -20,12 +20,12 @@ class CommandLine void operator=(CommandLine const&) = delete; const char* CmdLine() const; - bool GetOption(UINT id, const std::string** value) const; + bool GetOption(unsigned int id, const std::string** value) const; protected: struct Key { - UINT id; - PCSTR longName; + unsigned int id; + const char* longName; char shortName; bool hasArgument; }; @@ -36,9 +36,9 @@ class CommandLine }; std::vector