renamed logger into debug and start of piping through the GLFW input system to the script system
This commit is contained in:
parent
24154087ba
commit
b8f5681131
9 changed files with 193 additions and 169 deletions
|
@ -1,6 +1,6 @@
|
|||
|
||||
set(hdrs
|
||||
include/pw/core/log.hpp
|
||||
include/pw/core/debug.hpp
|
||||
include/pw/core/axisangle.hpp
|
||||
include/pw/core/core.hpp
|
||||
include/pw/core/math.hpp
|
||||
|
@ -17,7 +17,7 @@ set(hdrs
|
|||
|
||||
set(srcs
|
||||
src/image.cpp
|
||||
src/log.cpp
|
||||
src/debug.cpp
|
||||
src/core.cpp
|
||||
src/serialize.cpp
|
||||
src/timer.cpp
|
||||
|
|
|
@ -33,48 +33,13 @@
|
|||
|
||||
namespace pw {
|
||||
|
||||
class Log;
|
||||
|
||||
/**
|
||||
* @brief the streaming interface for the logger
|
||||
*/
|
||||
class LogStream {
|
||||
public:
|
||||
|
||||
|
||||
LogStream(Log* log = nullptr);
|
||||
~LogStream();
|
||||
LogStream(const LogStream& other);
|
||||
|
||||
|
||||
LogStream &operator << (const bool &value);
|
||||
LogStream &operator << (const char *value);
|
||||
LogStream& operator << (const std::string& value); ///! log a string
|
||||
|
||||
LogStream& operator << (const float &value); ///! log a float value
|
||||
LogStream& operator << (const double &value); ///! log a double value
|
||||
|
||||
LogStream& operator << (const int &value); ///! log a int value
|
||||
LogStream& operator << (const unsigned int &value); ///! log a int value
|
||||
|
||||
LogStream& operator << (const long &value); ///! log a long value
|
||||
LogStream& operator << (const unsigned long &value); ///! log a int value
|
||||
|
||||
LogStream& operator << (const void *value); ///! pointer
|
||||
|
||||
protected:
|
||||
|
||||
Log* _log;
|
||||
std::string _line;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief multipurpose logger used internally
|
||||
*/
|
||||
class Log {
|
||||
class debug {
|
||||
public:
|
||||
|
||||
enum LogLevel {
|
||||
enum level {
|
||||
kNone, //!< nothing will be logged, even no errors
|
||||
kError, //!< only errors will be logged
|
||||
kWarning, //!< log warnings (non-critical errors)
|
||||
|
@ -84,28 +49,62 @@ public:
|
|||
kAll = 0xFF //!< log absolutely everything
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief the streaming interface for the logger
|
||||
*/
|
||||
class stream {
|
||||
public:
|
||||
|
||||
|
||||
stream(debug* log = nullptr);
|
||||
~stream();
|
||||
stream(const stream& other);
|
||||
|
||||
|
||||
stream &operator << (const bool &value);
|
||||
stream &operator << (const char *value);
|
||||
stream& operator << (const std::string& value); ///! log a string
|
||||
|
||||
stream& operator << (const float &value); ///! log a float value
|
||||
stream& operator << (const double &value); ///! log a double value
|
||||
|
||||
stream& operator << (const int &value); ///! log a int value
|
||||
stream& operator << (const unsigned int &value); ///! log a int value
|
||||
|
||||
stream& operator << (const long &value); ///! log a long value
|
||||
stream& operator << (const unsigned long &value); ///! log a int value
|
||||
|
||||
stream& operator << (const void *value); ///! pointer
|
||||
|
||||
protected:
|
||||
|
||||
debug* _log;
|
||||
std::string _line;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** sets the logging level */
|
||||
void setLevel(LogLevel level) {_level = level;}
|
||||
void set_level(level level) {_level = level;}
|
||||
|
||||
/** gets the logging level */
|
||||
LogLevel level() const { return _level; }
|
||||
level level() const { return _level; }
|
||||
|
||||
/**
|
||||
* @brief get the stream interface of the logger
|
||||
* @return return a temporary object that will write and flush the logger
|
||||
*/
|
||||
static LogStream s(LogLevel level = kInfo);
|
||||
static stream s(enum level level = kInfo);
|
||||
|
||||
inline static LogStream d() { return s(Log::kInfo); }
|
||||
inline static LogStream e() { return s(Log::kError); }
|
||||
inline static LogStream w() { return s(Log::kWarning); }
|
||||
inline static stream d() { return s(debug::kInfo); }
|
||||
inline static stream e() { return s(debug::kError); }
|
||||
inline static stream w() { return s(debug::kWarning); }
|
||||
|
||||
/**
|
||||
* @brief returns the instance of the logger
|
||||
* @return
|
||||
*/
|
||||
static Log& get();
|
||||
static debug& get();
|
||||
|
||||
/**
|
||||
* @brief write a message to the log
|
||||
|
@ -118,46 +117,46 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
Log();
|
||||
~Log();
|
||||
debug();
|
||||
~debug();
|
||||
|
||||
CallbackList _callbacks;
|
||||
LogLevel _level;
|
||||
enum level _level;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief helper for changing the log level in a scope
|
||||
*/
|
||||
struct ScopeLogLevel
|
||||
{
|
||||
Log::LogLevel levelOutside;
|
||||
explicit ScopeLogLevel(Log::LogLevel level)
|
||||
{
|
||||
levelOutside = Log::get().level();
|
||||
Log::get().setLevel(level);
|
||||
}
|
||||
~ScopeLogLevel()
|
||||
{
|
||||
Log::get().setLevel(levelOutside);
|
||||
}
|
||||
};
|
||||
///**
|
||||
// * @brief helper for changing the log level in a scope
|
||||
// */
|
||||
//struct ScopeLogLevel
|
||||
//{
|
||||
// debug::level levelOutside;
|
||||
// explicit ScopeLogLevel(debug::level level)
|
||||
// {
|
||||
// levelOutside = debug::get().level();
|
||||
// debug::get().setLevel(level);
|
||||
// }
|
||||
// ~ScopeLogLevel()
|
||||
// {
|
||||
// debug::get().setLevel(levelOutside);
|
||||
// }
|
||||
//};
|
||||
|
||||
template <Log::LogLevel level>
|
||||
struct tpScopeLog {
|
||||
const char* info;
|
||||
explicit tpScopeLog(const char* i) : info(i) {
|
||||
Log::s(level) << info;
|
||||
}
|
||||
//template <debug::level level>
|
||||
//struct tpScopeLog {
|
||||
// const char* info;
|
||||
// explicit tpScopeLog(const char* i) : info(i) {
|
||||
// debug::s(level) << info;
|
||||
// }
|
||||
|
||||
~tpScopeLog() {
|
||||
Log::s(level) << info;
|
||||
}
|
||||
};
|
||||
// ~tpScopeLog() {
|
||||
// debug::s(level) << info;
|
||||
// }
|
||||
//};
|
||||
|
||||
// some macros
|
||||
|
||||
#define LOG_INFO() Log::s()
|
||||
#define LOG_FUNC() LOG_INFO() << __FUNCTION__ << " " << __LINE__ << " "
|
||||
//#define LOG_INFO() Log::s()
|
||||
//#define LOG_FUNC() LOG_INFO() << __FUNCTION__ << " " << __LINE__ << " "
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ typedef size_<real_t> size;
|
|||
|
||||
typedef size_<int> sizei;
|
||||
typedef size_<float> sizef;
|
||||
typedef size_<float> sized;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* (c) Copyrights 2007-2018 Hartmut Seichter
|
||||
*/
|
||||
#include "pw/core/log.hpp"
|
||||
#include "pw/core/debug.hpp"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
@ -59,8 +59,8 @@ struct tpFileLog {
|
|||
|
||||
|
||||
|
||||
Log::Log() :
|
||||
_level(Log::kNotify)
|
||||
debug::debug() :
|
||||
_level(debug::kNotify)
|
||||
{
|
||||
#if defined(_WINCE)
|
||||
_callbacks.add(new tpFileLog());
|
||||
|
@ -68,17 +68,17 @@ Log::Log() :
|
|||
_callbacks.push_back(tpConsoleLog());
|
||||
#endif
|
||||
// drop some info
|
||||
LogStream stream(this);
|
||||
stream stream(this);
|
||||
|
||||
// stream << "SSTT " << versionString(kVersionFull) << " " << std::thread::hardware_concurrency() << " threads";
|
||||
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
debug::~debug()
|
||||
{
|
||||
}
|
||||
|
||||
void Log::write(const std::string& message)
|
||||
void debug::write(const std::string& message)
|
||||
{
|
||||
for (CallbackList::iterator i = _callbacks.begin();
|
||||
i != _callbacks.end();
|
||||
|
@ -90,12 +90,13 @@ void Log::write(const std::string& message)
|
|||
|
||||
|
||||
/* static */
|
||||
LogStream Log::s(Log::LogLevel level) {
|
||||
return LogStream(&Log::get());
|
||||
debug::stream debug::s(enum debug::level level)
|
||||
{
|
||||
return stream(&debug::get());
|
||||
}
|
||||
|
||||
Log &Log::get() {
|
||||
static Log the_log;
|
||||
debug &debug::get() {
|
||||
static debug the_log;
|
||||
return the_log;
|
||||
}
|
||||
|
||||
|
@ -104,42 +105,42 @@ Log &Log::get() {
|
|||
// tpLogStream
|
||||
//
|
||||
|
||||
LogStream::LogStream(Log *log)
|
||||
debug::stream::stream(debug *log)
|
||||
: _log(log)
|
||||
{
|
||||
// _line.append(std::to_string(Timer::now(Timer::UnitSeconds)) + " ");
|
||||
}
|
||||
|
||||
LogStream::~LogStream()
|
||||
debug::stream::~stream()
|
||||
{
|
||||
_log->write(_line + "\n");
|
||||
}
|
||||
|
||||
LogStream::LogStream(const LogStream &other)
|
||||
debug::stream::stream(const stream &other)
|
||||
: _log(other._log)
|
||||
, _line(other._line)
|
||||
{
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const bool &value)
|
||||
debug::stream &debug::stream::operator <<(const bool &value)
|
||||
{
|
||||
_line.append(value ? "true" : "false");
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const char* value)
|
||||
debug::stream &debug::stream::operator <<(const char* value)
|
||||
{
|
||||
_line.append(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const std::string &value)
|
||||
debug::stream &debug::stream::operator <<(const std::string &value)
|
||||
{
|
||||
_line.append(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const float &value)
|
||||
debug::stream &debug::stream::operator <<(const float &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
@ -148,7 +149,7 @@ LogStream &LogStream::operator <<(const float &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const double &value)
|
||||
debug::stream &debug::stream::operator <<(const double &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
@ -157,7 +158,7 @@ LogStream &LogStream::operator <<(const double &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const int &value)
|
||||
debug::stream &debug::stream::operator <<(const int &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
@ -166,7 +167,7 @@ LogStream &LogStream::operator <<(const int &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const unsigned int &value)
|
||||
debug::stream &debug::stream::operator <<(const unsigned int &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
@ -175,7 +176,7 @@ LogStream &LogStream::operator <<(const unsigned int &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const long &value)
|
||||
debug::stream &debug::stream::operator <<(const long &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
@ -184,7 +185,7 @@ LogStream &LogStream::operator <<(const long &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const unsigned long &value)
|
||||
debug::stream &debug::stream::operator <<(const unsigned long &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
@ -193,7 +194,7 @@ LogStream &LogStream::operator <<(const unsigned long &value)
|
|||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator <<(const void *value)
|
||||
debug::stream &debug::stream::operator <<(const void *value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << value;
|
|
@ -10,46 +10,46 @@ namespace pw {
|
|||
void script_core::load(sol::table &ns)
|
||||
{
|
||||
|
||||
typedef double Scalar;
|
||||
typedef double Scalar;
|
||||
|
||||
ns.set("pi",pw::pi<Scalar>());
|
||||
ns.set("pi",pw::pi<Scalar>());
|
||||
|
||||
|
||||
ns.new_usertype<vector3d>("vector3",
|
||||
sol::constructors<vector3d(), vector3d(Scalar,Scalar,Scalar)>(),
|
||||
"set",&vector3d::set,
|
||||
"x", scripting::property(scripting::resolve<const Scalar&() const>(&vector3d::x), &vector3d::set_x),
|
||||
"y", scripting::property(scripting::resolve<const Scalar&() const>(&vector3d::y), &vector3d::set_y),
|
||||
"z", scripting::property(scripting::resolve<const Scalar&() const>(&vector3d::z), &vector3d::set_z),
|
||||
"norm",&vector3d::norm,
|
||||
"cross",&vector3d::cross,
|
||||
"dot",&vector3d::dot,
|
||||
// sol::meta_function::addition, sol::resolve<vector3d(const vector3d&, const vector3d&)>(::operator+),
|
||||
// sol::meta_function::subtraction, &vector3d::operator-
|
||||
// "v",&vector3d::values,
|
||||
"clone",&vector3d::clone
|
||||
);
|
||||
ns.new_usertype<vector3d>("vector3",
|
||||
sol::constructors<vector3d(), vector3d(Scalar,Scalar,Scalar)>(),
|
||||
"set",&vector3d::set,
|
||||
"x", scripting::property(scripting::resolve<const Scalar&() const>(&vector3d::x), &vector3d::set_x),
|
||||
"y", scripting::property(scripting::resolve<const Scalar&() const>(&vector3d::y), &vector3d::set_y),
|
||||
"z", scripting::property(scripting::resolve<const Scalar&() const>(&vector3d::z), &vector3d::set_z),
|
||||
"norm",&vector3d::norm,
|
||||
"cross",&vector3d::cross,
|
||||
"dot",&vector3d::dot,
|
||||
// sol::meta_function::addition, sol::resolve<vector3d(const vector3d&, const vector3d&)>(::operator+),
|
||||
// sol::meta_function::subtraction, &vector3d::operator-
|
||||
// "v",&vector3d::values,
|
||||
"clone",&vector3d::clone
|
||||
);
|
||||
|
||||
ns.new_usertype<quaterniond>("quaternion",
|
||||
sol::constructors<quaterniond(), quaterniond(Scalar,Scalar,Scalar,Scalar)>(),
|
||||
"set",&quaterniond::set,
|
||||
"x", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::x), &quaterniond::set_x),
|
||||
"y", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::y), &quaterniond::set_y),
|
||||
"z", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::z), &quaterniond::set_z),
|
||||
"w", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::w), &quaterniond::set_w),
|
||||
"dot",&quaterniond::dot,
|
||||
"inverse",scripting::readonly_property(&quaterniond::inverse),
|
||||
"normalized",&quaterniond::normalized,
|
||||
"lerp",&quaterniond::lerp
|
||||
// "v",&vector3d::values,
|
||||
// "clone",&vector3d::clone
|
||||
);
|
||||
ns.new_usertype<quaterniond>("quaternion",
|
||||
sol::constructors<quaterniond(), quaterniond(Scalar,Scalar,Scalar,Scalar)>(),
|
||||
"set",&quaterniond::set,
|
||||
"x", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::x), &quaterniond::set_x),
|
||||
"y", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::y), &quaterniond::set_y),
|
||||
"z", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::z), &quaterniond::set_z),
|
||||
"w", scripting::property(scripting::resolve<const Scalar&() const>(&quaterniond::w), &quaterniond::set_w),
|
||||
"dot",&quaterniond::dot,
|
||||
"inverse",scripting::readonly_property(&quaterniond::inverse),
|
||||
"normalized",&quaterniond::normalized,
|
||||
"lerp",&quaterniond::lerp
|
||||
// "v",&vector3d::values,
|
||||
// "clone",&vector3d::clone
|
||||
);
|
||||
|
||||
ns.new_usertype<axisangled>("axisangle",
|
||||
sol::constructors<axisangled(), axisangled(vector3d,Scalar)>(),
|
||||
"axis",scripting::property(&axisangled::axis,&axisangled::set_axis),
|
||||
"angle",scripting::property(&axisangled::angle,&axisangled::set_angle)
|
||||
);
|
||||
ns.new_usertype<axisangled>("axisangle",
|
||||
sol::constructors<axisangled(), axisangled(vector3d,Scalar)>(),
|
||||
"axis",scripting::property(&axisangled::axis,&axisangled::set_axis),
|
||||
"angle",scripting::property(&axisangled::angle,&axisangled::set_angle)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
#include "script_system.hpp"
|
||||
|
||||
#include "pw/system/window.hpp"
|
||||
#include "pw/system/input.hpp"
|
||||
|
||||
|
||||
namespace pw {
|
||||
|
||||
void script_system::load(sol::table &ns)
|
||||
{
|
||||
ns.new_usertype<window>("window",
|
||||
"update",&window::update,
|
||||
"title",sol::writeonly_property(&window::set_title),
|
||||
"set_size",&window::set_size
|
||||
);
|
||||
ns.new_usertype<window>("window",
|
||||
"update",&window::update,
|
||||
"title",sol::writeonly_property(&window::set_title),
|
||||
"set_size",&window::set_size
|
||||
);
|
||||
|
||||
// ns.new_usertype<input>("input",
|
||||
// "new", sol::no_constructor,
|
||||
// "get",&input::get
|
||||
// );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
|
||||
set(hdrs
|
||||
include/pw/system/window.hpp
|
||||
)
|
||||
include/pw/system/window.hpp
|
||||
include/pw/system/input.hpp
|
||||
)
|
||||
|
||||
set(srcs
|
||||
src/window.cpp
|
||||
)
|
||||
src/window.cpp
|
||||
src/input.cpp
|
||||
)
|
||||
|
||||
add_library(pwsystem
|
||||
STATIC
|
||||
${hdrs}
|
||||
${srcs}
|
||||
)
|
||||
STATIC
|
||||
${hdrs}
|
||||
${srcs}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
pwsystem
|
||||
PUBLIC
|
||||
include
|
||||
)
|
||||
pwsystem
|
||||
PUBLIC
|
||||
include
|
||||
)
|
||||
|
||||
target_link_libraries(pwsystem pwcore pwvisual glfw glad)
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef PW_WINDOW_HPP
|
||||
#define PW_WINDOW_HPP
|
||||
#ifndef PW_SYSTEM_WINDOW_HPP
|
||||
#define PW_SYSTEM_WINDOW_HPP
|
||||
|
||||
#include <pw/core/globals.hpp>
|
||||
#include <pw/core/size.hpp>
|
||||
|
||||
namespace pw {
|
||||
|
||||
|
@ -14,12 +15,12 @@ public:
|
|||
bool update();
|
||||
|
||||
void set_title(const std::string& title);
|
||||
|
||||
void set_size(int w, int h);
|
||||
size get_size() const;
|
||||
|
||||
// context* get_context();
|
||||
typedef void drop_callback;
|
||||
|
||||
int get_heigth();
|
||||
int get_width();
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//#include "glad/glad.h"
|
||||
|
||||
#include "pw/visual/context.hpp"
|
||||
#include "pw/system/input.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -34,10 +35,18 @@ struct window::impl {
|
|||
|
||||
// window_context _context;
|
||||
|
||||
|
||||
static void key_callback(GLFWwindow *window,int key, int scancode, int action, int mods)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
window::impl* impl = static_cast<window::impl*>(glfwGetWindowUserPointer(window));
|
||||
// impl->on_resize(width,height);
|
||||
// impl->on_resize(width,height);
|
||||
|
||||
// std::cout << "framebuffer " << width << "x" << height << std::endl;
|
||||
// glViewport(0, 0, width, height);
|
||||
|
@ -50,7 +59,9 @@ struct window::impl {
|
|||
_window = glfwCreateWindow(640, 480, "pixwerxs", nullptr, nullptr);
|
||||
|
||||
glfwSetWindowUserPointer(_window,this);
|
||||
|
||||
glfwSetFramebufferSizeCallback(_window, window::impl::framebuffer_size_callback);
|
||||
glfwSetKeyCallback(_window, window::impl::key_callback);
|
||||
|
||||
|
||||
#if 0
|
||||
|
@ -70,6 +81,7 @@ struct window::impl {
|
|||
{
|
||||
if (!glfwWindowShouldClose(_window)) {
|
||||
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
// do other stuff
|
||||
|
@ -97,6 +109,14 @@ struct window::impl {
|
|||
glfwSetWindowSize(_window,w,h);
|
||||
}
|
||||
|
||||
::pw::size size() const {
|
||||
|
||||
int w,h;
|
||||
glfwGetWindowSize(_window,&w,&h);
|
||||
|
||||
return ::pw::size(w,h);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
@ -125,19 +145,11 @@ void window::set_size(int w,int h)
|
|||
_impl->set_size(w,h);
|
||||
}
|
||||
|
||||
int window::get_width()
|
||||
size window::get_size() const
|
||||
{
|
||||
// int w,h;
|
||||
// glfwGetWindowSize(_window,&w,&h);
|
||||
// return w;
|
||||
}
|
||||
|
||||
int window::get_heigth()
|
||||
{
|
||||
// int w,h;
|
||||
// glfwGetWindowSize(_window,&w,&h);
|
||||
// return h;
|
||||
return _impl->size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue