From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- game/server/NavUI/NavUI.cpp | 892 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 892 insertions(+) create mode 100644 game/server/NavUI/NavUI.cpp (limited to 'game/server/NavUI/NavUI.cpp') diff --git a/game/server/NavUI/NavUI.cpp b/game/server/NavUI/NavUI.cpp new file mode 100644 index 0000000..ce93f12 --- /dev/null +++ b/game/server/NavUI/NavUI.cpp @@ -0,0 +1,892 @@ +//-------------------------------------------------------------------------------------------------------- +//========= Copyright Valve Corporation, All rights reserved. ============// + +#include "cbase.h" + +#ifdef SERVER_USES_VGUI + +#include "NavUI.h" +#include "filesystem.h" +#include "tier0/icommandline.h" +#include "vgui_gamedll_int.h" +#include "ienginevgui.h" +#include "IGameUIFuncs.h" +#include "fmtstr.h" +#include "NavMenu.h" +#include + +#include "SelectionTool.h" +#include "MeshTool.h" +#include "AttributeTool.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; +class CNavUIBasePanel; +class CNavUIToolPanel; + +extern IGameUIFuncs *gameuifuncs; + + +//-------------------------------------------------------------------------------------------------------- +static CNavUIBasePanel *s_navUIPanel = NULL; +CNavUIBasePanel *TheNavUI( void ) +{ + return s_navUIPanel; +} + + +//-------------------------------------------------------------------------------------------------------- +ConVar NavGUIRebuild( "nav_gui_rebuild", "0", FCVAR_CHEAT, "Rebuilds the nav ui windows from scratch every time they're opened" ); + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIButton::LookupKey( void ) +{ + if ( m_hideKey == BUTTON_CODE_INVALID ) + m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID; +} + + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIButton::OnKeyCodePressed( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + m_hidePressedTimer.Start(); + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIButton::OnKeyCodeReleased( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f ) + { + s_navUIPanel->ToggleVisibility(); + m_hidePressedTimer.Invalidate(); + } + return; + } + + BaseClass::OnKeyCodeReleased( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUITextEntry::LookupKey( void ) +{ + if ( m_hideKey == BUTTON_CODE_INVALID ) + m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID; +} + + + +//-------------------------------------------------------------------------------------------------------- +void CNavUITextEntry::OnKeyCodePressed( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + m_hidePressedTimer.Start(); + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUITextEntry::OnKeyCodeReleased( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f ) + { + s_navUIPanel->ToggleVisibility(); + m_hidePressedTimer.Invalidate(); + } + return; + } + + BaseClass::OnKeyCodeReleased( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIComboBox::LookupKey( void ) +{ + if ( m_hideKey == BUTTON_CODE_INVALID ) + m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID; +} + + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIComboBox::OnKeyCodePressed( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + m_hidePressedTimer.Start(); + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIComboBox::OnKeyCodeReleased( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f ) + { + s_navUIPanel->ToggleVisibility(); + m_hidePressedTimer.Invalidate(); + } + return; + } + + BaseClass::OnKeyCodeReleased( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUICheckButton::LookupKey( void ) +{ + if ( m_hideKey == BUTTON_CODE_INVALID ) + m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID; +} + + + +//-------------------------------------------------------------------------------------------------------- +void CNavUICheckButton::OnKeyCodePressed( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + m_hidePressedTimer.Start(); + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUICheckButton::OnKeyCodeReleased( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f ) + { + s_navUIPanel->ToggleVisibility(); + m_hidePressedTimer.Invalidate(); + } + return; + } + + BaseClass::OnKeyCodeReleased( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +CNavUIBasePanel::CNavUIBasePanel() : vgui::Frame( NULL, "NavUI" ) +{ + m_hideKey = BUTTON_CODE_INVALID; + SetScheme( "SourceScheme" ); + LoadControlSettings( "Resource/UI/NavUI.res" ); + SetAlpha( 0 ); + SetMouseInputEnabled( false ); + SetSizeable( false ); + SetMoveable( false ); + SetCloseButtonVisible( false ); + SetTitleBarVisible( false ); + + SetLeftClickAction( "", "" ); + + m_hidden = false; + m_toolPanel = NULL; + m_selectionPanel = NULL; + + SetTitle( "", true); + + m_dragSelecting = m_dragUnselecting = false; + + MenuButton *menuButton = dynamic_cast< MenuButton * >(FindChildByName( "FileMenuButton" )); + if ( menuButton ) + { + NavMenu * menu = new NavMenu( menuButton, "NavFileMenu" ); + menu->AddMenuItem( "Quit", "Quit", new KeyValues( "Command", "command", "StopEditing" ), this ); + menuButton->SetMenu( menu ); + menuButton->SetOpenDirection( Menu::DOWN ); + } + + menuButton = dynamic_cast< MenuButton * >(FindChildByName( "SelectionMenuButton" )); + if ( menuButton ) + { + NavMenu * menu = new NavMenu( menuButton, "NavSelectionMenu" ); + menu->AddMenuItem( "Flood Select", "Flood Select", new KeyValues( "Command", "command", "FloodSelect" ), this ); + menu->AddMenuItem( "Flood Select (fog)", "Flood Select (Fog)", new KeyValues( "Command", "command", "FloodSelect fog" ), this ); + menuButton->SetMenu( menu ); + menuButton->SetOpenDirection( Menu::DOWN ); + } +} + + +//-------------------------------------------------------------------------------------------------------- +CNavUIBasePanel::~CNavUIBasePanel() +{ + s_navUIPanel = NULL; +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::SetLeftClickAction( const char *action, const char *text ) +{ + if ( !action || !*action ) + { + action = "Selection::Select"; + } + + if ( !text || !*text ) + { + text = "Select"; + } + + V_strncpy( m_leftClickAction, action, sizeof( m_leftClickAction ) ); + m_performingLeftClickAction = false; + + vgui::Label *label = dynamic_cast< vgui::Label * >(FindChildByName( "LeftClick" ) ); + if ( label ) + { + label->SetText( UTIL_VarArgs( "Left Click: %s", text ) ); + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetBgColor( Color( 0, 0, 0, 0 ) ); + + Panel *panel = FindChildByName( "SidebarParent" ); + if ( panel ) + { + panel->SetBgColor( Color( 128, 128, 128, 255 ) ); + panel->SetPaintBackgroundType( 2 ); + } + + panel = FindChildByName( "MenuParent" ); + if ( panel ) + { + panel->SetBgColor( Color( 128, 128, 128, 255 ) ); + panel->SetPaintBackgroundType( 0 ); + } + + panel = FindChildByName( "ToolParent" ); + if ( panel ) + { + panel->SetBgColor( Color( 0, 0, 0, 128 ) ); + panel->SetPaintBackgroundType( 0 ); + } + + panel = FindChildByName( "SelectionParent" ); + if ( panel ) + { + panel->SetBgColor( Color( 0, 0, 0, 128 ) ); + panel->SetPaintBackgroundType( 0 ); + } + + panel = FindChildByName( "MouseFeedbackParent" ); + if ( panel ) + { + panel->SetBgColor( Color( 0, 0, 0, 128 ) ); + panel->SetPaintBackgroundType( 0 ); + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::PerformLayout( void ) +{ + int wide, tall; + vgui::surface()->GetScreenSize( wide, tall ); + SetBounds( 0, 0, wide, tall ); + + Panel *panel = FindChildByName( "MenuParent" ); + if ( panel ) + { + int oldWide, oldTall; + panel->GetSize( oldWide, oldTall ); + panel->SetSize( wide, oldTall ); + } + + BaseClass::PerformLayout(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::PaintBackground( void ) +{ + BaseClass::PaintBackground(); +} + + +//-------------------------------------------------------------------------------------------------------- +const char *CNavUIBasePanel::ActiveToolName( void ) const +{ + if ( m_toolPanel ) + return m_toolPanel->GetName(); + + return ""; +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::ActivateTool( const char *toolName ) +{ + if ( m_toolPanel && FStrEq( m_toolPanel->GetName(), toolName ) ) + { + m_toolPanel->Shutdown(); + m_toolPanel->MarkForDeletion(); + m_toolPanel = NULL; + } + else + { + if ( m_toolPanel ) + { + m_toolPanel->Shutdown(); + m_toolPanel->MarkForDeletion(); + m_toolPanel = NULL; + } + + Panel *toolParent = FindChildByName( "ToolParent" ); + if ( !toolParent ) + toolParent = this; + + m_toolPanel = CreateTool( toolName, toolParent ); + if ( m_toolPanel ) + { + m_toolPanel->Init(); + m_toolPanel->SetVisible( true ); + } + } +} + + +//-------------------------------------------------------------------------------------------------------- +CNavUIToolPanel *CNavUIBasePanel::CreateTool( const char *toolName, vgui::Panel *toolParent ) +{ + if ( FStrEq( toolName, "Selection" ) ) + { + return new SelectionToolPanel( toolParent, toolName ); + } + if ( FStrEq( toolName, "Mesh" ) ) + { + return new MeshToolPanel( toolParent, toolName ); + } + if ( FStrEq( toolName, "Attribute" ) ) + { + return new AttributeToolPanel( toolParent, toolName ); + } + return NULL; +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnCommand( const char *command ) +{ + CSplitString argv( command, " " ); + + if ( FStrEq( "Close", command ) ) + { + ToggleVisibility(); + } + else if ( FStrEq( "MeshTool", command ) ) + { + ActivateTool( "Mesh" ); + return; + } + else if ( FStrEq( "AttributesTool", command ) ) + { + ActivateTool( "Attribute" ); + return; + } + else if ( FStrEq( "StopEditing", command ) ) + { + MarkForDeletion(); + engine->ServerCommand( "nav_edit 0\n" ); + } + else + { + BaseClass::OnCommand( command ); + } + + // argv can't delete individual elements +} + + +//-------------------------------------------------------------------------------------------------------- +// GameUI panels are always visible by default, so here we hide ourselves if the GameUI is up. +void CNavUIBasePanel::OnTick( void ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( !player || !player->IsConnected() ) + { + m_hidden = true; + SetVisible( false ); + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + return; + } + + if ( enginevgui->IsGameUIVisible() ) + { + if ( GetAlpha() != 0 ) + { + SetAlpha( 0 ); + SetMouseInputEnabled( false ); + } + } + else + { + if ( m_hidden ) + { + if ( GetAlpha() > 0 ) + { + SetAlpha( 0 ); + SetMouseInputEnabled( false ); + } + + SetVisible( false ); + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + return; + } + else + { + if ( GetAlpha() < 255 ) + { + SetAlpha( 255 ); + SetMouseInputEnabled( true ); + + if ( !m_selectionPanel ) + { + Panel *selectionParent = FindChildByName( "SelectionParent" ); + if ( !selectionParent ) + selectionParent = this; + + m_selectionPanel = CreateTool( "Selection", selectionParent ); + if ( m_selectionPanel ) + { + m_selectionPanel->Init(); + m_selectionPanel->SetVisible( true ); + } + } + } + + CFmtStr str; + if ( m_toolPanel ) + { + str.sprintf( "%s - %s", STRING( gpGlobals->mapname ), m_toolPanel->GetName() ); + } + else + { + str.sprintf( "%s", STRING( gpGlobals->mapname ) ); + } + SetTitle( str.Access(), true ); + } + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::ToggleVisibility( void ) +{ + m_hidden = !m_hidden; + + if ( m_hidden && NavGUIRebuild.GetBool() ) + { + MarkForDeletion(); + s_navUIPanel = NULL; + } +} + + +//-------------------------------------------------------------------------------------------------------- +Panel *CNavUIBasePanel::CreateControlByName( const char *controlName ) +{ + if ( FStrEq( controlName, "Button" ) ) + { + return new CNavUIButton( this, "CNavUIButton" ); + } + + if ( FStrEq( controlName, "TextEntry" ) ) + { + return new CNavUITextEntry( this, "CNavUITextEntry" ); + } + + if ( FStrEq( controlName, "ComboBox" ) ) + { + return new CNavUIComboBox( this, "CNavUIComboBox", 5, false ); + } + + if ( FStrEq( controlName, "CheckButton" ) ) + { + return new CNavUICheckButton( this, "CNavUICheckButton", "" ); + } + + return BaseClass::CreateControlByName( controlName ); +} + + +//-------------------------------------------------------------------------------------------------------- +Panel *CNavUIToolPanel::CreateControlByName( const char *controlName ) +{ + if ( s_navUIPanel ) + { + return s_navUIPanel->CreateControlByName( controlName ); + } + + return BaseClass::CreateControlByName( controlName ); +} + + +//-------------------------------------------------------------------------------------------------------- +bool CNavUIToolPanel::IsCheckButtonChecked( const char *name ) +{ + vgui::CheckButton *checkButton = dynamic_cast< vgui::CheckButton * >( FindChildByName( name, true ) ); + if ( !checkButton ) + return false; + + return checkButton->IsSelected(); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::LookupKey( void ) +{ + if ( m_hideKey == BUTTON_CODE_INVALID ) + m_hideKey = (gameuifuncs) ? gameuifuncs->GetButtonCodeForBind( "nav_gui" ) : BUTTON_CODE_INVALID; +} + + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnKeyCodePressed( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + m_hidePressedTimer.Start(); + return; + } + + BaseClass::OnKeyCodePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnKeyCodeReleased( KeyCode code ) +{ + LookupKey(); + + if ( code == m_hideKey ) + { + if ( m_hidePressedTimer.HasStarted() && m_hidePressedTimer.GetElapsedTime() < 0.5f ) + { + s_navUIPanel->ToggleVisibility(); + m_hidePressedTimer.Invalidate(); + } + return; + } + + BaseClass::OnKeyCodeReleased( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnCursorEntered( void ) +{ + BaseClass::OnCursorEntered(); + + if ( m_performingLeftClickAction ) + { + if ( m_toolPanel ) + { + m_toolPanel->FinishLeftClickAction( m_leftClickAction ); + } + if ( m_selectionPanel ) + { + m_selectionPanel->FinishLeftClickAction( m_leftClickAction ); + } + m_performingLeftClickAction = false; + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnCursorMoved( int x, int y ) +{ + if ( m_toolPanel ) + { + m_toolPanel->OnCursorMoved( x, y ); + } + if ( m_selectionPanel ) + { + m_selectionPanel->OnCursorMoved( x, y ); + } + + BaseClass::OnCursorMoved( x, y ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnCursorExited( void ) +{ + BaseClass::OnCursorExited(); + + if ( m_performingLeftClickAction ) + { + if ( m_toolPanel ) + { + m_toolPanel->FinishLeftClickAction( m_leftClickAction ); + } + if ( m_selectionPanel ) + { + m_selectionPanel->FinishLeftClickAction( m_leftClickAction ); + } + m_performingLeftClickAction = false; + } + /* + if ( m_dragSelecting || m_dragUnselecting ) + { + PlaySound( "EDIT_END_AREA.Creating" ); + } + m_dragSelecting = false; + m_dragUnselecting = false; + */ +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnMousePressed( MouseCode code ) +{ + /* + CNavArea *area = TheNavMesh->GetSelectedArea(); + */ + + switch ( code ) + { + case MOUSE_LEFT: + m_performingLeftClickAction = true; + if ( m_toolPanel ) + { + m_toolPanel->StartLeftClickAction( m_leftClickAction ); + } + if ( m_selectionPanel ) + { + m_selectionPanel->StartLeftClickAction( m_leftClickAction ); + } + break; + } + + BaseClass::OnMousePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::OnMouseReleased( MouseCode code ) +{ + switch ( code ) + { + case MOUSE_LEFT: + if ( m_performingLeftClickAction ) + { + if ( m_toolPanel ) + { + m_toolPanel->FinishLeftClickAction( m_leftClickAction ); + } + if ( m_selectionPanel ) + { + m_selectionPanel->FinishLeftClickAction( m_leftClickAction ); + } + m_performingLeftClickAction = false; + } + break; + case MOUSE_RIGHT: + if ( m_toolPanel ) + { + m_toolPanel->StartRightClickAction( "Selection::ClearSelection" ); + } + if ( m_selectionPanel ) + { + m_selectionPanel->StartRightClickAction( "Selection::ClearSelection" ); + } + break; + } + + BaseClass::OnMousePressed( code ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavUIBasePanel::PlaySound( const char *sound ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( player ) + { + player->EmitSound( sound ); + } +} + + +//-------------------------------------------------------------------------------------------------------- +// Taken from cl_dll/view.cpp +float ScaleFOVByWidthRatio( float fovDegrees, float ratio ) +{ + float halfAngleRadians = fovDegrees * ( 0.5f * M_PI / 180.0f ); + float t = tan( halfAngleRadians ); + t *= ratio; + float retDegrees = ( 180.0f / M_PI ) * atan( t ); + return retDegrees * 2.0f; +} + + +//-------------------------------------------------------------------------------------------------------- +// Purpose: +// Given a field of view and mouse/screen positions as well as the current +// render origin and angles, returns a unit vector through the mouse position +// that can be used to trace into the world under the mouse click pixel. +// Input : +// mousex - +// mousey - +// fov - +// vecRenderOrigin - +// vecRenderAngles - +// Output : +// vecPickingRay +// Adapted from cl_dll/c_vguiscreen.cpp +//-------------------------------------------------------------------------------------------------------- +void ScreenToWorld( int mousex, int mousey, float fov, + const Vector& vecRenderOrigin, + const QAngle& vecRenderAngles, + Vector& vecPickingRay ) +{ + float dx, dy; + float c_x, c_y; + float dist; + Vector vpn, vup, vright; + + int wide, tall; + vgui::surface()->GetScreenSize( wide, tall ); + c_x = wide / 2; + c_y = tall / 2; + + float scaled_fov = ScaleFOVByWidthRatio( fov, (float)wide / (float)tall * 0.75f ); + + dx = (float)mousex - c_x; + // Invert Y + dy = c_y - (float)mousey; + + // Convert view plane distance + dist = c_x / tan( M_PI * scaled_fov / 360.0 ); + + // Decompose view angles + AngleVectors( vecRenderAngles, &vpn, &vright, &vup ); + + // Offset forward by view plane distance, and then by pixel offsets + vecPickingRay = vpn * dist + vright * ( dx ) + vup * ( dy ); + + // Convert to unit vector + VectorNormalize( vecPickingRay ); +} + + +//-------------------------------------------------------------------------------------------------------- +void GetNavUIEditVectors( Vector *pos, Vector *forward ) +{ + CBasePlayer *player = UTIL_GetListenServerHost(); + if ( !player ) + { + return; + } + + if ( !s_navUIPanel ) + { + return; + } + + if ( s_navUIPanel->GetAlpha() < 255 ) + { + return; + } + + int x, y; + vgui::surface()->SurfaceGetCursorPos( x, y ); + + float fov = player->GetFOV(); + QAngle eyeAngles = player->EyeAngles(); + Vector eyePosition = player->EyePosition(); + + Vector pick; + ScreenToWorld( x, y, fov, eyePosition, eyeAngles, pick ); + + *forward = pick; +} + + +//-------------------------------------------------------------------------------------------------------- +void NavUICommand( void ) +{ + if ( engine->IsDedicatedServer() ) + return; + + if ( UTIL_GetCommandClient() != UTIL_GetListenServerHost() ) + return; + + engine->ServerCommand( "nav_edit 1\n" ); + bool created = false; + if ( !s_navUIPanel ) + { + created = true; + s_navUIPanel = CreateNavUI(); + } + + ShowGameDLLPanel( s_navUIPanel ); + vgui::ivgui()->AddTickSignal( s_navUIPanel->GetVPanel() ); + if ( !created ) + { + s_navUIPanel->ToggleVisibility(); + } +} +ConCommand nav_gui( "nav_gui", NavUICommand, "Opens the nav editing GUI", FCVAR_CHEAT ); + +#endif // SERVER_USES_VGUI + +//-------------------------------------------------------------------------------------------------------- -- cgit v1.2.3