diff options
Diffstat (limited to 'vgui2/src')
31 files changed, 16834 insertions, 0 deletions
diff --git a/vgui2/src/Bitmap.cpp b/vgui2/src/Bitmap.cpp new file mode 100644 index 0000000..4d4a71e --- /dev/null +++ b/vgui2/src/Bitmap.cpp @@ -0,0 +1,272 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <vgui/ISurface.h> +#include "bitmap.h" +#include "vgui_internal.h" +#include "filesystem.h" +#include "tier1/utlbuffer.h" +#include <tier0/dbg.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : *filename - image file to load +//----------------------------------------------------------------------------- +Bitmap::Bitmap(const char *filename, bool hardwareFiltered) +{ + _filtered = hardwareFiltered; + + int size = strlen(filename) + 1; + _filename = (char *)malloc( size ); + Assert( _filename ); + + Q_snprintf( _filename, size, "%s", filename ); + + _bProcedural = false; + + if ( Q_stristr( filename, ".pic" ) ) + { + _bProcedural = true; + } + + _id = 0; + _uploaded = false; + _color = Color(255, 255, 255, 255); + _pos[0] = _pos[1] = 0; + _valid = true; + _wide = 0; + _tall = 0; + nFrameCache = 0; + _rotation = 0; + + ForceUpload(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Bitmap::~Bitmap() +{ + Evict(); + + if ( _filename ) + { + free( _filename ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void Bitmap::GetSize(int &wide, int &tall) +{ + wide = 0; + tall = 0; + + if ( !_valid ) + return; + + // if a size has not been set, get it from the texture + if ( 0 == _wide && 0 ==_tall ) + { + g_pSurface->DrawGetTextureSize(_id, _wide, _tall); + + } + wide = _wide; + tall = _tall; +} + +//----------------------------------------------------------------------------- +// Purpose: size of the bitmap +//----------------------------------------------------------------------------- +void Bitmap::GetContentSize(int &wide, int &tall) +{ + GetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: ignored +//----------------------------------------------------------------------------- +void Bitmap::SetSize(int x, int y) +{ +// AssertMsg( _filtered, "Bitmap::SetSize called on non-hardware filtered texture. Bitmap can't be scaled; you don't want to be calling this." ); + _wide = x; + _tall = y; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void Bitmap::SetPos(int x, int y) +{ + _pos[0] = x; + _pos[1] = y; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void Bitmap::SetColor(Color col) +{ + _color = col; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the file name of the bitmap +//----------------------------------------------------------------------------- +const char *Bitmap::GetName() +{ + return _filename; +} + +//----------------------------------------------------------------------------- +// Purpose: Renders the loaded image, uploading it if necessary +// Assumes a valid image is always returned from uploading +//----------------------------------------------------------------------------- +void Bitmap::Paint() +{ + if ( !_valid ) + return; + + // if we don't have an _id then lets make one + if ( !_id ) + { + _id = g_pSurface->CreateNewTextureID(); + } + + // if we have not uploaded yet, lets go ahead and do so + if ( !_uploaded ) + { + ForceUpload(); + } + + // set the texture current, set the color, and draw the biatch + g_pSurface->DrawSetColor( _color[0], _color[1], _color[2], _color[3] ); + g_pSurface->DrawSetTexture( _id ); + + if ( _wide == 0 ) + { + GetSize( _wide, _tall); + } + + if ( _rotation == ROTATED_UNROTATED ) + { + g_pSurface->DrawTexturedRect(_pos[0], _pos[1], _pos[0] + _wide, _pos[1] + _tall); + } + else + { + vgui::Vertex_t verts[4]; + verts[0].m_Position.Init( 0, 0 ); + verts[1].m_Position.Init( _wide, 0 ); + verts[2].m_Position.Init( _wide, _tall ); + verts[3].m_Position.Init( 0, _tall ); + + switch ( _rotation ) + { + case ROTATED_CLOCKWISE_90: + verts[0].m_TexCoord.Init( 1, 0 ); + verts[1].m_TexCoord.Init( 1, 1 ); + verts[2].m_TexCoord.Init( 0, 1 ); + verts[3].m_TexCoord.Init( 0, 0 ); + break; + + case ROTATED_ANTICLOCKWISE_90: + verts[0].m_TexCoord.Init( 0, 1 ); + verts[1].m_TexCoord.Init( 0, 0 ); + verts[2].m_TexCoord.Init( 1, 0 ); + verts[3].m_TexCoord.Init( 1, 1 ); + break; + + case ROTATED_FLIPPED: + verts[0].m_TexCoord.Init( 1, 1 ); + verts[1].m_TexCoord.Init( 0, 1 ); + verts[2].m_TexCoord.Init( 0, 0 ); + verts[3].m_TexCoord.Init( 1, 0 ); + break; + + default: + case ROTATED_UNROTATED: + break; + } + + g_pSurface->DrawTexturedPolygon( 4, verts ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: ensures the bitmap has been uploaded +//----------------------------------------------------------------------------- +void Bitmap::ForceUpload() +{ + if ( !_valid || _uploaded ) + return; + + if ( !_id ) + { + _id = g_pSurface->CreateNewTextureID( _bProcedural ); + } + + if ( !_bProcedural ) + { + g_pSurface->DrawSetTextureFile( _id, _filename, _filtered, false ); + } + + _uploaded = true; + _valid = g_pSurface->IsTextureIDValid( _id ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +HTexture Bitmap::GetID() +{ + return _id; +} + +bool Bitmap::Evict() +{ + if ( _id != 0 ) + { + g_pSurface->DestroyTextureID( _id ); + // purposely not resetting _valid to match existing silly logic + // either a Paint() or ForceUpload() will re-establish + _id = 0; + _uploaded = false; + return true; + } + return false; +} + +int Bitmap::GetNumFrames() +{ + if ( !_valid ) + return 0; + + return g_pSurface->GetTextureNumFrames( _id ); +} + +void Bitmap::SetFrame( int nFrame ) +{ + if ( !_valid ) + return; + + // the frame cache is critical to cheapen the cost of this call + g_pSurface->DrawSetTextureFrame( _id, nFrame, &nFrameCache ); +} + + + + + diff --git a/vgui2/src/Border.cpp b/vgui2/src/Border.cpp new file mode 100644 index 0000000..de7cfff --- /dev/null +++ b/vgui2/src/Border.cpp @@ -0,0 +1,270 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <stdio.h> +#include <string.h> + +#include "vgui/IPanel.h" +#include "vgui/IScheme.h" +#include "vgui/ISurface.h" + +#include "VGUI_Border.h" +#include "vgui_internal.h" +#include "VPanel.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Border::Border() +{ + _inset[0]=0; + _inset[1]=0; + _inset[2]=0; + _inset[3]=0; + _name = NULL; + m_eBackgroundType = IBorder::BACKGROUND_FILLED; + + memset(_sides, 0, sizeof(_sides)); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Border::~Border() +{ + delete [] _name; + + for (int i = 0; i < 4; i++) + { + delete [] _sides[i].lines; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Border::SetInset(int left,int top,int right,int bottom) +{ + _inset[SIDE_LEFT] = left; + _inset[SIDE_TOP] = top; + _inset[SIDE_RIGHT] = right; + _inset[SIDE_BOTTOM] = bottom; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Border::GetInset(int& left,int& top,int& right,int& bottom) +{ + left = _inset[SIDE_LEFT]; + top = _inset[SIDE_TOP]; + right = _inset[SIDE_RIGHT]; + bottom = _inset[SIDE_BOTTOM]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Border::Paint(int x, int y, int wide, int tall) +{ + Paint(x, y, wide, tall, -1, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the border with the specified size +//----------------------------------------------------------------------------- +void Border::Paint(int x, int y, int wide, int tall, int breakSide, int breakStart, int breakEnd) +{ + // iterate through and draw all lines + // draw left + int i; + for (i = 0; i < _sides[SIDE_LEFT].count; i++) + { + line_t *line = &(_sides[SIDE_LEFT].lines[i]); + g_pSurface->DrawSetColor(line->col[0], line->col[1], line->col[2], line->col[3]); + + if (breakSide == SIDE_LEFT) + { + // split into two section + if (breakStart > 0) + { + // draw before the break Start + g_pSurface->DrawFilledRect(x + i, y + line->startOffset, x + i + 1, y + breakStart); + } + + if (breakEnd < (tall - line->endOffset)) + { + // draw after break end + g_pSurface->DrawFilledRect(x + i, y + breakEnd + 1, x + i + 1, tall - line->endOffset); + } + } + else + { + g_pSurface->DrawFilledRect(x + i, y + line->startOffset, x + i + 1, tall - line->endOffset); + } + } + + // draw top + for (i = 0; i < _sides[SIDE_TOP].count; i++) + { + line_t *line = &(_sides[SIDE_TOP].lines[i]); + g_pSurface->DrawSetColor(line->col[0], line->col[1], line->col[2], line->col[3]); + + if (breakSide == SIDE_TOP) + { + // split into two section + if (breakStart > 0) + { + // draw before the break Start + g_pSurface->DrawFilledRect(x + line->startOffset, y + i, x + breakStart, y + i + 1); + } + + if (breakEnd < (wide - line->endOffset)) + { + // draw after break end + g_pSurface->DrawFilledRect(x + breakEnd + 1, y + i, wide - line->endOffset, y + i + 1); + } + } + else + { + g_pSurface->DrawFilledRect(x + line->startOffset, y + i, wide - line->endOffset, y + i + 1); + } + } + + // draw right + for (i = 0; i < _sides[SIDE_RIGHT].count; i++) + { + line_t *line = &(_sides[SIDE_RIGHT].lines[i]); + g_pSurface->DrawSetColor(line->col[0], line->col[1], line->col[2], line->col[3]); + g_pSurface->DrawFilledRect(wide - (i+1), y + line->startOffset, (wide - (i+1)) + 1, tall - line->endOffset); + } + + // draw bottom + for (i = 0; i < _sides[SIDE_BOTTOM].count; i++) + { + line_t *line = &(_sides[SIDE_BOTTOM].lines[i]); + g_pSurface->DrawSetColor(line->col[0], line->col[1], line->col[2], line->col[3]); + g_pSurface->DrawFilledRect(x + line->startOffset, tall - (i+1), wide - line->endOffset, (tall - (i+1)) + 1); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Border::Paint(VPANEL panel) +{ + // get panel size + int wide, tall; + ((VPanel *)panel)->GetSize(wide, tall); + Paint(0, 0, wide, tall, -1, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Border::ApplySchemeSettings(IScheme *pScheme, KeyValues *inResourceData) +{ + // load inset information + const char *insetString = inResourceData->GetString("inset", "0 0 0 0"); + + int left, top, right, bottom; + GetInset(left, top, right, bottom); + sscanf(insetString, "%d %d %d %d", &left, &top, &right, &bottom); + SetInset(left, top, right, bottom); + + // get the border information from the scheme + ParseSideSettings(SIDE_LEFT, inResourceData->FindKey("Left"),pScheme); + ParseSideSettings(SIDE_TOP, inResourceData->FindKey("Top"),pScheme); + ParseSideSettings(SIDE_RIGHT, inResourceData->FindKey("Right"),pScheme); + ParseSideSettings(SIDE_BOTTOM, inResourceData->FindKey("Bottom"),pScheme); + + m_eBackgroundType = (backgroundtype_e)inResourceData->GetInt("backgroundtype"); +} + +//----------------------------------------------------------------------------- +// Purpose: parses scheme data +//----------------------------------------------------------------------------- +void Border::ParseSideSettings(int side_index, KeyValues *inResourceData, IScheme *pScheme) +{ + if (!inResourceData) + return; + + // count the numeber of lines in the side + int count = 0; + KeyValues *kv; + for (kv = inResourceData->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + count++; + } + + // allocate memory + _sides[side_index].count = count; + _sides[side_index].lines = new line_t[count]; + + // iterate through the keys + //!! this loads in order, ignoring key names + int index = 0; + for (kv = inResourceData->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + line_t *line = &(_sides[side_index].lines[index]); + + // this is the color name, get that from the color table + const char *col = kv->GetString("color", NULL); + line->col = pScheme->GetColor(col, Color(0, 0, 0, 0)); + + col = kv->GetString("offset", NULL); + int Start = 0, end = 0; + if (col) + { + sscanf(col, "%d %d", &Start, &end); + } + line->startOffset = Start; + line->endOffset = end; + + index++; + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *Border::GetName() +{ + if (_name) + return _name; + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void Border::SetName(const char *name) +{ + if (_name) + { + delete [] _name; + } + + int len = Q_strlen(name) + 1; + _name = new char[ len ]; + Q_strncpy( _name, name, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBorder::backgroundtype_e Border::GetBackgroundType() +{ + return m_eBackgroundType; +} + diff --git a/vgui2/src/IMessageListener.h b/vgui2/src/IMessageListener.h new file mode 100644 index 0000000..0ff6fc0 --- /dev/null +++ b/vgui2/src/IMessageListener.h @@ -0,0 +1,43 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMESSAGELISTENER_H +#define IMESSAGELISTENER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> + +class KeyValues; + +namespace vgui +{ + +enum MessageSendType_t +{ + MESSAGE_SENT = 0, + MESSAGE_POSTED, + MESSAGE_RECEIVED +}; + +class VPanel; + + +class IMessageListener +{ +public: + virtual void Message( VPanel* pSender, VPanel* pReceiver, + KeyValues* pKeyValues, MessageSendType_t type ) = 0; +}; + +IMessageListener* MessageListener(); + +} + +#endif // IMESSAGELISTENER_H diff --git a/vgui2/src/ImageBorder.cpp b/vgui2/src/ImageBorder.cpp new file mode 100644 index 0000000..51cb0fd --- /dev/null +++ b/vgui2/src/ImageBorder.cpp @@ -0,0 +1,223 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <stdio.h> +#include <string.h> + +#include <vgui_controls/Panel.h> +#include "vgui/IPanel.h" +#include "vgui/IScheme.h" +#include "vgui/ISurface.h" + +#include "vgui_internal.h" +#include "ImageBorder.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ImageBorder::ImageBorder() +{ + _name = NULL; + m_eBackgroundType = IBorder::BACKGROUND_TEXTURED; + + m_pszImageName = NULL; + m_iTextureID = g_pSurface->CreateNewTextureID(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ImageBorder::~ImageBorder() +{ + if ( vgui::surface() && m_iTextureID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iTextureID ); + m_iTextureID = -1; + } + + delete [] _name; + if ( m_pszImageName ) + { + delete [] m_pszImageName; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImageBorder::SetImage(const char *imageName) +{ + if ( m_pszImageName ) + { + delete [] m_pszImageName; + m_pszImageName = NULL; + } + + if (*imageName) + { + int len = Q_strlen(imageName) + 1 + 5; // 5 for "vgui/" + delete [] m_pszImageName; + m_pszImageName = new char[ len ]; + Q_snprintf( m_pszImageName, len, "vgui/%s", imageName ); + + g_pSurface->DrawSetTextureFile( m_iTextureID, m_pszImageName, true, false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImageBorder::SetInset(int left,int top,int right,int bottom) +{ + _inset[SIDE_LEFT] = left; + _inset[SIDE_TOP] = top; + _inset[SIDE_RIGHT] = right; + _inset[SIDE_BOTTOM] = bottom; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImageBorder::GetInset(int& left,int& top,int& right,int& bottom) +{ + left = _inset[SIDE_LEFT]; + top = _inset[SIDE_TOP]; + right = _inset[SIDE_RIGHT]; + bottom = _inset[SIDE_BOTTOM]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImageBorder::Paint(int x, int y, int wide, int tall) +{ + Paint(x, y, wide, tall, -1, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the border with the specified size +//----------------------------------------------------------------------------- +void ImageBorder::Paint(int x, int y, int wide, int tall, int breakSide, int breakStart, int breakEnd) +{ + if ( !m_pszImageName || !m_pszImageName[0] ) + return; + + g_pSurface->DrawSetColor( 255, 255, 255, 255 ); + g_pSurface->DrawSetTexture( m_iTextureID ); + + float uvx = 0; + float uvy = 0; + float uvw = 1.0; + float uvh = 1.0; + Vector2D uv11( uvx, uvy ); + Vector2D uv21( uvx+uvw, uvy ); + Vector2D uv22( uvx+uvw, uvy+uvh ); + Vector2D uv12( uvx, uvy+uvh ); + + if ( m_bTiled ) + { + int imageWide, imageTall; + g_pSurface->DrawGetTextureSize( m_iTextureID, imageWide, imageTall ); + + int y = 0; + while ( y < tall ) + { + int x = 0; + while (x < wide) + { + vgui::Vertex_t verts[4]; + verts[0].Init( Vector2D( x, y ), uv11 ); + verts[1].Init( Vector2D( x+imageWide, y ), uv21 ); + verts[2].Init( Vector2D( x+imageWide, y+imageTall ), uv22 ); + verts[3].Init( Vector2D( x, y+imageTall ), uv12 ); + + g_pSurface->DrawTexturedPolygon( 4, verts ); + + x += imageWide; + } + + y += imageTall; + } + } + else + { + vgui::Vertex_t verts[4]; + verts[0].Init( Vector2D( x, y ), uv11 ); + verts[1].Init( Vector2D( x+wide, y ), uv21 ); + verts[2].Init( Vector2D( x+wide, y+tall ), uv22 ); + verts[3].Init( Vector2D( x, y+tall ), uv12 ); + + g_pSurface->DrawTexturedPolygon( 4, verts ); + } + + g_pSurface->DrawSetTexture(0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImageBorder::Paint(VPANEL panel) +{ + // get panel size + int wide, tall; + ipanel()->GetSize( panel, wide, tall ); + Paint(0, 0, wide, tall, -1, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ImageBorder::ApplySchemeSettings(IScheme *pScheme, KeyValues *inResourceData) +{ + m_eBackgroundType = (backgroundtype_e)inResourceData->GetInt("backgroundtype"); + m_bTiled = inResourceData->GetInt( "tiled" ); + + const char *imageName = inResourceData->GetString("image", ""); + SetImage( imageName ); + + m_bPaintFirst = inResourceData->GetInt("paintfirst", true ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *ImageBorder::GetName() +{ + if (_name) + return _name; + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ImageBorder::SetName(const char *name) +{ + if (_name) + { + delete [] _name; + } + + int len = Q_strlen(name) + 1; + _name = new char[ len ]; + Q_strncpy( _name, name, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBorder::backgroundtype_e ImageBorder::GetBackgroundType() +{ + return m_eBackgroundType; +} + diff --git a/vgui2/src/ImageBorder.h b/vgui2/src/ImageBorder.h new file mode 100644 index 0000000..25e9e86 --- /dev/null +++ b/vgui2/src/ImageBorder.h @@ -0,0 +1,69 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Core implementation of vgui +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMAGE_BORDER_H +#define IMAGE_BORDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui/IBorder.h> +#include <vgui/IScheme.h> +#include <vgui/IPanel.h> +#include <Color.h> + +class KeyValues; + +//----------------------------------------------------------------------------- +// Purpose: Custom border that renders itself with images +//----------------------------------------------------------------------------- +class ImageBorder : public vgui::IBorder +{ +public: + ImageBorder(); + ~ImageBorder(); + + virtual void Paint(vgui::VPANEL panel); + virtual void Paint(int x0, int y0, int x1, int y1); + virtual void Paint(int x0, int y0, int x1, int y1, int breakSide, int breakStart, int breakStop); + virtual void SetInset(int left, int top, int right, int bottom); + virtual void GetInset(int &left, int &top, int &right, int &bottom); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme, KeyValues *inResourceData); + + virtual const char *GetName(); + virtual void SetName(const char *name); + virtual backgroundtype_e GetBackgroundType(); + + virtual bool PaintFirst( void ) { return m_bPaintFirst; } + +protected: + void SetImage(const char *imageName); + +protected: + int _inset[4]; + +private: + // protected copy constructor to prevent use + ImageBorder(ImageBorder&); + + char *_name; + + backgroundtype_e m_eBackgroundType; + + friend class VPanel; + + int m_iTextureID; + bool m_bTiled; + + char *m_pszImageName; + bool m_bPaintFirst; +}; + +#endif // IMAGE_BORDER_H diff --git a/vgui2/src/InputWin32.cpp b/vgui2/src/InputWin32.cpp new file mode 100644 index 0000000..a9a037f --- /dev/null +++ b/vgui2/src/InputWin32.cpp @@ -0,0 +1,3198 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#if defined( WIN32 ) && !defined( _X360 ) +#include <windows.h> +#include <imm.h> +#define DO_IME +#endif + +#include <string.h> + +#include "vgui_internal.h" +#include "VPanel.h" +#include "utlvector.h" +#include <KeyValues.h> +#include "tier0/vcrmode.h" + +#include <vgui/VGUI.h> +#include <vgui/ISystem.h> +#include <vgui/IClientPanel.h> +#include <vgui/IInputInternal.h> +#include <vgui/IPanel.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/KeyCode.h> +#include <vgui/MouseCode.h> +#include "vgui/Cursor.h" +#include <vgui/keyrepeat.h> + +#include "utllinkedlist.h" +#include "tier0/icommandline.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +/* +> Subject: RE: l4d2 & motd +> +> From: Alfred Reynolds +> I'd go with the if it ain't broke don't touch it route, might as well +> leave win32 as is and just knobble the asserts where we know we won't implement it. +> +>> From: Mike Sartain +>> Well now that's interesting. Is it ok to remove it for win32 then? +>> +>>> From: Alfred Reynolds +>>> We never did the IME work, AFAIK it only ever worked on the game's +>>> console in game which isn't useful for users. So, no demand, hard +>>> (actually, really hard) to implement so it wasn't done. +>>> +>>>> From: Mike Sartain +>>>> There are also a bunch of IME Language functions in +>>>> vgui2/src/inputwin32.cpp that are NYI on Linux as well - but it looks +>>>> like those haven't ever been implemented on OSX either. Alfred, what +>>>> is the story there? +*/ +#if 0 // !defined( DO_IME ) && !defined( _X360 ) +#define ASSERT_IF_IME_NYI() Assert( !"IME Support NYI" ) +#else +#define ASSERT_IF_IME_NYI() +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +bool IsDispatchingMessageQueue( void ); + +using namespace vgui; + +class CInputSystem : public IInputInternal +{ +public: + CInputSystem(); + ~CInputSystem(); + + virtual void RunFrame(); + + virtual void PanelDeleted(VPANEL panel); + + virtual void UpdateMouseFocus(int x, int y); + virtual void SetMouseFocus(VPANEL newMouseFocus); + + virtual void SetCursorPos(int x, int y); + virtual void UpdateCursorPosInternal( int x, int y ); + virtual void GetCursorPos(int &x, int &y); + virtual void SetCursorOveride(HCursor cursor); + virtual HCursor GetCursorOveride(); + + + virtual void SetMouseCapture(VPANEL panel); + + virtual VPANEL GetFocus(); + virtual VPANEL GetCalculatedFocus(); + virtual VPANEL GetMouseOver(); + + virtual bool WasMousePressed(MouseCode code); + virtual bool WasMouseDoublePressed(MouseCode code); + virtual bool IsMouseDown(MouseCode code); + virtual bool WasMouseReleased(MouseCode code); + virtual bool WasKeyPressed(KeyCode code); + virtual bool IsKeyDown(KeyCode code); + virtual bool WasKeyTyped(KeyCode code); + virtual bool WasKeyReleased(KeyCode code); + + virtual void GetKeyCodeText(KeyCode code, char *buf, int buflen); + + virtual bool InternalCursorMoved(int x,int y); //expects input in surface space + virtual bool InternalMousePressed(MouseCode code); + virtual bool InternalMouseDoublePressed(MouseCode code); + virtual bool InternalMouseReleased(MouseCode code); + virtual bool InternalMouseWheeled(int delta); + virtual bool InternalKeyCodePressed(KeyCode code); + virtual void InternalKeyCodeTyped(KeyCode code); + virtual void InternalKeyTyped(wchar_t unichar); + virtual bool InternalKeyCodeReleased(KeyCode code); + virtual void SetKeyCodeState( KeyCode code, bool bPressed ); + virtual void SetMouseCodeState( MouseCode code, MouseCodeState_t state ); + virtual void UpdateButtonState( const InputEvent_t &event ); + + virtual VPANEL GetAppModalSurface(); + // set the modal dialog panel. + // all events will go only to this panel and its children. + virtual void SetAppModalSurface(VPANEL panel); + // release the modal dialog panel + // do this when your modal dialog finishes. + virtual void ReleaseAppModalSurface(); + + // returns true if the specified panel is a child of the current modal panel + // if no modal panel is set, then this always returns TRUE + virtual bool IsChildOfModalPanel(VPANEL panel, bool checkModalSubTree = true ); + + // Creates/ destroys "input" contexts, which contains information + // about which controls have mouse + key focus, for example. + virtual HInputContext CreateInputContext(); + virtual void DestroyInputContext( HInputContext context ); + + // Associates a particular panel with an input context + // Associating NULL is valid; it disconnects the panel from the context + virtual void AssociatePanelWithInputContext( HInputContext context, VPANEL pRoot ); + + // Activates a particular input context, use DEFAULT_INPUT_CONTEXT + // to get the one normally used by VGUI + virtual void ActivateInputContext( HInputContext context ); + virtual void PostCursorMessage( ); + virtual void HandleExplicitSetCursor( ); + + virtual void ResetInputContext( HInputContext context ); + + virtual void GetCursorPosition( int &x, int &y ); + + virtual void SetIMEWindow( void *hwnd ); + virtual void *GetIMEWindow(); + + // Change keyboard layout type + virtual void OnChangeIME( bool forward ); + virtual int GetCurrentIMEHandle(); + virtual int GetEnglishIMEHandle(); + + // Returns the Language Bar label (Chinese, Korean, Japanese, Russion, Thai, etc.) + virtual void GetIMELanguageName( wchar_t *buf, int unicodeBufferSizeInBytes ); + // Returns the short code for the language (EN, CH, KO, JP, RU, TH, etc. ). + virtual void GetIMELanguageShortCode( wchar_t *buf, int unicodeBufferSizeInBytes ); + + // Call with NULL dest to get item count + virtual int GetIMELanguageList( LanguageItem *dest, int destcount ); + virtual int GetIMEConversionModes( ConversionModeItem *dest, int destcount ); + virtual int GetIMESentenceModes( SentenceModeItem *dest, int destcount ); + + virtual void OnChangeIMEByHandle( int handleValue ); + virtual void OnChangeIMEConversionModeByHandle( int handleValue ); + virtual void OnChangeIMESentenceModeByHandle( int handleValue ); + + virtual void OnInputLanguageChanged(); + virtual void OnIMEStartComposition(); + virtual void OnIMEComposition( int flags ); + virtual void OnIMEEndComposition(); + + virtual void OnIMEShowCandidates(); + virtual void OnIMEChangeCandidates(); + virtual void OnIMECloseCandidates(); + + virtual void OnIMERecomputeModes(); + + virtual int GetCandidateListCount(); + virtual void GetCandidate( int num, wchar_t *dest, int destSizeBytes ); + virtual int GetCandidateListSelectedItem(); + virtual int GetCandidateListPageSize(); + virtual int GetCandidateListPageStart(); + + virtual void SetCandidateWindowPos( int x, int y ); + virtual bool GetShouldInvertCompositionString(); + virtual bool CandidateListStartsAtOne(); + + virtual void SetCandidateListPageStart( int start ); + + // Passes in a keycode which allows hitting other mouse buttons w/o cancelling capture mode + virtual void SetMouseCaptureEx(VPANEL panel, MouseCode captureStartMouseCode ); + + virtual void RegisterKeyCodeUnhandledListener( VPANEL panel ); + virtual void UnregisterKeyCodeUnhandledListener( VPANEL panel ); + + // Posts unhandled message to all interested panels + virtual void OnKeyCodeUnhandled( int keyCode ); + + // Assumes subTree is a child panel of the root panel for the vgui contect + // if restrictMessagesToSubTree is true, then mouse and kb messages are only routed to the subTree and it's children and mouse/kb focus + // can only be on one of the subTree children, if a mouse click occurs outside of the subtree, and "UnhandledMouseClick" message is sent to unhandledMouseClickListener panel + // if it's set + // if restrictMessagesToSubTree is false, then mouse and kb messages are routed as normal except that they are not routed down into the subtree + // however, if a mouse click occurs outside of the subtree, and "UnhandleMouseClick" message is sent to unhandledMouseClickListener panel + // if it's set + virtual void SetModalSubTree( VPANEL subTree, VPANEL unhandledMouseClickListener, bool restrictMessagesToSubTree = true ); + virtual void ReleaseModalSubTree(); + virtual VPANEL GetModalSubTree(); + + // These toggle whether the modal subtree is exclusively receiving messages or conversely whether it's being excluded from receiving messages + virtual void SetModalSubTreeReceiveMessages( bool state ); + virtual bool ShouldModalSubTreeReceiveMessages() const; + + virtual VPANEL GetMouseCapture(); + + virtual VPANEL GetMouseFocus(); +private: + + VPanel *GetMouseFocusIgnoringModalSubtree(); + + void InternalSetCompositionString( const wchar_t *compstr ); + void InternalShowCandidateWindow(); + void InternalHideCandidateWindow(); + void InternalUpdateCandidateWindow(); + + bool PostKeyMessage(KeyValues *message); + + void DestroyCandidateList(); + void CreateNewCandidateList(); + + VPanel *CalculateNewKeyFocus(); + + void PostModalSubTreeMessage( VPanel *subTree, bool state ); + // returns true if the specified panel is a child of the current modal panel + // if no modal panel is set, then this always returns TRUE + bool IsChildOfModalSubTree(VPANEL panel); + + void SurfaceSetCursorPos( int x, int y ); + void SurfaceGetCursorPos( int &x, int &y ); + + struct InputContext_t + { + VPANEL _rootPanel; + + bool _mousePressed[MOUSE_COUNT]; + bool _mouseDoublePressed[MOUSE_COUNT]; + bool _mouseDown[MOUSE_COUNT]; + bool _mouseReleased[MOUSE_COUNT]; + bool _keyPressed[BUTTON_CODE_COUNT]; + bool _keyTyped[BUTTON_CODE_COUNT]; + bool _keyDown[BUTTON_CODE_COUNT]; + bool _keyReleased[BUTTON_CODE_COUNT]; + + VPanel *_keyFocus; + VPanel *_oldMouseFocus; + VPanel *_mouseFocus; // the panel that has the current mouse focus - same as _mouseOver unless _mouseCapture is set + VPanel *_mouseOver; // the panel that the mouse is currently over, NULL if not over any vgui item + + VPanel *_mouseCapture; // the panel that has currently captured mouse focus + MouseCode m_MouseCaptureStartCode; // The Mouse button which was pressed to initiate mouse capture + VPanel *_appModalPanel; // the modal dialog panel. + + int m_nCursorX; + int m_nCursorY; + + int m_nLastPostedCursorX; + int m_nLastPostedCursorY; + + int m_nExternallySetCursorX; + int m_nExternallySetCursorY; + bool m_bSetCursorExplicitly; + + CUtlVector< VPanel * > m_KeyCodeUnhandledListeners; + + VPanel *m_pModalSubTree; + VPanel *m_pUnhandledMouseClickListener; + bool m_bRestrictMessagesToModalSubTree; + + CKeyRepeatHandler m_keyRepeater; + }; + + void InitInputContext( InputContext_t *pContext ); + InputContext_t *GetInputContext( HInputContext context ); + void PanelDeleted(VPANEL focus, InputContext_t &context); + + HCursor _cursorOverride; + + const char *_keyTrans[KEY_LAST]; + + InputContext_t m_DefaultInputContext; + HInputContext m_hContext; // current input context + + CUtlLinkedList< InputContext_t, HInputContext > m_Contexts; + +#ifdef DO_IME + void *_imeWnd; + CANDIDATELIST *_imeCandidates; +#endif + + int m_nDebugMessages; +}; + +CInputSystem g_Input; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CInputSystem, IInput, VGUI_INPUT_INTERFACE_VERSION, g_Input); // export IInput to everyone else, not IInputInternal! +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CInputSystem, IInputInternal, VGUI_INPUTINTERNAL_INTERFACE_VERSION, g_Input); // for use in external surfaces only! (like the engine surface) + +namespace vgui +{ +vgui::IInputInternal *g_pInput = &g_Input; +} + + +CInputSystem::CInputSystem() +{ + m_nDebugMessages = -1; +#ifdef DO_IME + _imeWnd = null; + _imeCandidates = null; +#endif + InitInputContext( &m_DefaultInputContext ); + m_hContext = DEFAULT_INPUT_CONTEXT; + + // build key to text translation table + // first byte unshifted key + // second byte shifted key + // the rest is the name of the key + _keyTrans[KEY_0] ="0)KEY_0"; + _keyTrans[KEY_1] ="1!KEY_1"; + _keyTrans[KEY_2] ="2@KEY_2"; + _keyTrans[KEY_3] ="3#KEY_3"; + _keyTrans[KEY_4] ="4$KEY_4"; + _keyTrans[KEY_5] ="5%KEY_5"; + _keyTrans[KEY_6] ="6^KEY_6"; + _keyTrans[KEY_7] ="7&KEY_7"; + _keyTrans[KEY_8] ="8*KEY_8"; + _keyTrans[KEY_9] ="9(KEY_9"; + _keyTrans[KEY_A] ="aAKEY_A"; + _keyTrans[KEY_B] ="bBKEY_B"; + _keyTrans[KEY_C] ="cCKEY_C"; + _keyTrans[KEY_D] ="dDKEY_D"; + _keyTrans[KEY_E] ="eEKEY_E"; + _keyTrans[KEY_F] ="fFKEY_F"; + _keyTrans[KEY_G] ="gGKEY_G"; + _keyTrans[KEY_H] ="hHKEY_H"; + _keyTrans[KEY_I] ="iIKEY_I"; + _keyTrans[KEY_J] ="jJKEY_J"; + _keyTrans[KEY_K] ="kKKEY_K"; + _keyTrans[KEY_L] ="lLKEY_L"", L"; + _keyTrans[KEY_M] ="mMKEY_M"; + _keyTrans[KEY_N] ="nNKEY_N"; + _keyTrans[KEY_O] ="oOKEY_O"; + _keyTrans[KEY_P] ="pPKEY_P"; + _keyTrans[KEY_Q] ="qQKEY_Q"; + _keyTrans[KEY_R] ="rRKEY_R"; + _keyTrans[KEY_S] ="sSKEY_S"; + _keyTrans[KEY_T] ="tTKEY_T"; + _keyTrans[KEY_U] ="uUKEY_U"; + _keyTrans[KEY_V] ="vVKEY_V"; + _keyTrans[KEY_W] ="wWKEY_W"; + _keyTrans[KEY_X] ="xXKEY_X"; + _keyTrans[KEY_Y] ="yYKEY_Y"; + _keyTrans[KEY_Z] ="zZKEY_Z"; + _keyTrans[KEY_PAD_0] ="0\0KEY_PAD_0"; + _keyTrans[KEY_PAD_1] ="1\0KEY_PAD_1"; + _keyTrans[KEY_PAD_2] ="2\0KEY_PAD_2"; + _keyTrans[KEY_PAD_3] ="3\0KEY_PAD_3"; + _keyTrans[KEY_PAD_4] ="4\0KEY_PAD_4"; + _keyTrans[KEY_PAD_5] ="5\0KEY_PAD_5"; + _keyTrans[KEY_PAD_6] ="6\0KEY_PAD_6"; + _keyTrans[KEY_PAD_7] ="7\0KEY_PAD_7"; + _keyTrans[KEY_PAD_8] ="8\0KEY_PAD_8"; + _keyTrans[KEY_PAD_9] ="9\0KEY_PAD_9"; + _keyTrans[KEY_PAD_DIVIDE] ="//KEY_PAD_DIVIDE"; + _keyTrans[KEY_PAD_MULTIPLY] ="**KEY_PAD_MULTIPLY"; + _keyTrans[KEY_PAD_MINUS] ="--KEY_PAD_MINUS"; + _keyTrans[KEY_PAD_PLUS] ="++KEY_PAD_PLUS"; + _keyTrans[KEY_PAD_ENTER] ="\0\0KEY_PAD_ENTER"; + _keyTrans[KEY_PAD_DECIMAL] =".\0KEY_PAD_DECIMAL"", L"; + _keyTrans[KEY_LBRACKET] ="[{KEY_LBRACKET"; + _keyTrans[KEY_RBRACKET] ="]}KEY_RBRACKET"; + _keyTrans[KEY_SEMICOLON] =";:KEY_SEMICOLON"; + _keyTrans[KEY_APOSTROPHE] ="'\"KEY_APOSTROPHE"; + _keyTrans[KEY_BACKQUOTE] ="`~KEY_BACKQUOTE"; + _keyTrans[KEY_COMMA] =",<KEY_COMMA"; + _keyTrans[KEY_PERIOD] =".>KEY_PERIOD"; + _keyTrans[KEY_SLASH] ="/?KEY_SLASH"; + _keyTrans[KEY_BACKSLASH] ="\\|KEY_BACKSLASH"; + _keyTrans[KEY_MINUS] ="-_KEY_MINUS"; + _keyTrans[KEY_EQUAL] ="=+KEY_EQUAL"", L"; + _keyTrans[KEY_ENTER] ="\0\0KEY_ENTER"; + _keyTrans[KEY_SPACE] =" KEY_SPACE"; + _keyTrans[KEY_BACKSPACE] ="\0\0KEY_BACKSPACE"; + _keyTrans[KEY_TAB] ="\0\0KEY_TAB"; + _keyTrans[KEY_CAPSLOCK] ="\0\0KEY_CAPSLOCK"; + _keyTrans[KEY_NUMLOCK] ="\0\0KEY_NUMLOCK"; + _keyTrans[KEY_ESCAPE] ="\0\0KEY_ESCAPE"; + _keyTrans[KEY_SCROLLLOCK] ="\0\0KEY_SCROLLLOCK"; + _keyTrans[KEY_INSERT] ="\0\0KEY_INSERT"; + _keyTrans[KEY_DELETE] ="\0\0KEY_DELETE"; + _keyTrans[KEY_HOME] ="\0\0KEY_HOME"; + _keyTrans[KEY_END] ="\0\0KEY_END"; + _keyTrans[KEY_PAGEUP] ="\0\0KEY_PAGEUP"; + _keyTrans[KEY_PAGEDOWN] ="\0\0KEY_PAGEDOWN"; + _keyTrans[KEY_BREAK] ="\0\0KEY_BREAK"; + _keyTrans[KEY_LSHIFT] ="\0\0KEY_LSHIFT"; + _keyTrans[KEY_RSHIFT] ="\0\0KEY_RSHIFT"; + _keyTrans[KEY_LALT] ="\0\0KEY_LALT"; + _keyTrans[KEY_RALT] ="\0\0KEY_RALT"; + _keyTrans[KEY_LCONTROL] ="\0\0KEY_LCONTROL"", L"; + _keyTrans[KEY_RCONTROL] ="\0\0KEY_RCONTROL"", L"; + _keyTrans[KEY_LWIN] ="\0\0KEY_LWIN"; + _keyTrans[KEY_RWIN] ="\0\0KEY_RWIN"; + _keyTrans[KEY_APP] ="\0\0KEY_APP"; + _keyTrans[KEY_UP] ="\0\0KEY_UP"; + _keyTrans[KEY_LEFT] ="\0\0KEY_LEFT"; + _keyTrans[KEY_DOWN] ="\0\0KEY_DOWN"; + _keyTrans[KEY_RIGHT] ="\0\0KEY_RIGHT"; + _keyTrans[KEY_F1] ="\0\0KEY_F1"; + _keyTrans[KEY_F2] ="\0\0KEY_F2"; + _keyTrans[KEY_F3] ="\0\0KEY_F3"; + _keyTrans[KEY_F4] ="\0\0KEY_F4"; + _keyTrans[KEY_F5] ="\0\0KEY_F5"; + _keyTrans[KEY_F6] ="\0\0KEY_F6"; + _keyTrans[KEY_F7] ="\0\0KEY_F7"; + _keyTrans[KEY_F8] ="\0\0KEY_F8"; + _keyTrans[KEY_F9] ="\0\0KEY_F9"; + _keyTrans[KEY_F10] ="\0\0KEY_F10"; + _keyTrans[KEY_F11] ="\0\0KEY_F11"; + _keyTrans[KEY_F12] ="\0\0KEY_F12"; +} + +CInputSystem::~CInputSystem() +{ + DestroyCandidateList(); +} + +//----------------------------------------------------------------------------- +// Resets an input context +//----------------------------------------------------------------------------- +void CInputSystem::InitInputContext( InputContext_t *pContext ) +{ + pContext->_rootPanel = NULL; + pContext->_keyFocus = NULL; + pContext->_oldMouseFocus = NULL; + pContext->_mouseFocus = NULL; + pContext->_mouseOver = NULL; + pContext->_mouseCapture = NULL; + pContext->_appModalPanel = NULL; + + pContext->m_nCursorX = pContext->m_nCursorY = 0; + pContext->m_nLastPostedCursorX = pContext->m_nLastPostedCursorY = -9999; + pContext->m_nExternallySetCursorX = pContext->m_nExternallySetCursorY = 0; + pContext->m_bSetCursorExplicitly = false; + + // zero mouse and keys + memset(pContext->_mousePressed, 0, sizeof(pContext->_mousePressed)); + memset(pContext->_mouseDoublePressed, 0, sizeof(pContext->_mouseDoublePressed)); + memset(pContext->_mouseDown, 0, sizeof(pContext->_mouseDown)); + memset(pContext->_mouseReleased, 0, sizeof(pContext->_mouseReleased)); + memset(pContext->_keyPressed, 0, sizeof(pContext->_keyPressed)); + memset(pContext->_keyTyped, 0, sizeof(pContext->_keyTyped)); + memset(pContext->_keyDown, 0, sizeof(pContext->_keyDown)); + memset(pContext->_keyReleased, 0, sizeof(pContext->_keyReleased)); + + pContext->m_MouseCaptureStartCode = (MouseCode)-1; + + pContext->m_KeyCodeUnhandledListeners.RemoveAll(); + + pContext->m_pModalSubTree = NULL; + pContext->m_pUnhandledMouseClickListener = NULL; + pContext->m_bRestrictMessagesToModalSubTree = false; +} + +void CInputSystem::ResetInputContext( HInputContext context ) +{ + // FIXME: Needs to release various keys, mouse buttons, etc...? + // At least needs to cause things to lose focus + InitInputContext( GetInputContext(context) ); +} + + +//----------------------------------------------------------------------------- +// Creates/ destroys "input" contexts, which contains information +// about which controls have mouse + key focus, for example. +//----------------------------------------------------------------------------- +HInputContext CInputSystem::CreateInputContext() +{ + HInputContext i = m_Contexts.AddToTail(); + InitInputContext( &m_Contexts[i] ); + return i; +} + +void CInputSystem::DestroyInputContext( HInputContext context ) +{ + Assert( context != DEFAULT_INPUT_CONTEXT ); + if ( m_hContext == context ) + { + ActivateInputContext( DEFAULT_INPUT_CONTEXT ); + } + m_Contexts.Remove(context); +} + + +//----------------------------------------------------------------------------- +// Returns the current input context +//----------------------------------------------------------------------------- +CInputSystem::InputContext_t *CInputSystem::GetInputContext( HInputContext context ) +{ + if (context == DEFAULT_INPUT_CONTEXT) + return &m_DefaultInputContext; + return &m_Contexts[context]; +} + + +//----------------------------------------------------------------------------- +// Associates a particular panel with an input context +// Associating NULL is valid; it disconnects the panel from the context +//----------------------------------------------------------------------------- +void CInputSystem::AssociatePanelWithInputContext( HInputContext context, VPANEL pRoot ) +{ + // Changing the root panel should invalidate keysettings, etc. + if (GetInputContext(context)->_rootPanel != pRoot) + { + ResetInputContext( context ); + GetInputContext(context)->_rootPanel = pRoot; + } +} + + +//----------------------------------------------------------------------------- +// Activates a particular input context, use DEFAULT_INPUT_CONTEXT +// to get the one normally used by VGUI +//----------------------------------------------------------------------------- +void CInputSystem::ActivateInputContext( HInputContext context ) +{ + Assert( (context == DEFAULT_INPUT_CONTEXT) || m_Contexts.IsValidIndex(context) ); + m_hContext = context; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInputSystem::RunFrame() +{ + if ( m_nDebugMessages == -1 ) + { + m_nDebugMessages = CommandLine()->FindParm( "-vguifocus" ) ? 1 : 0; + } + + InputContext_t *pContext = GetInputContext(m_hContext); + + // tick whoever has the focus + if (pContext->_keyFocus) + { + // when modal dialogs are up messages only get sent to the dialogs children. + if (IsChildOfModalPanel((VPANEL)pContext->_keyFocus)) + { + g_pIVgui->PostMessage((VPANEL)pContext->_keyFocus, new KeyValues("KeyFocusTicked"), NULL); + } + } + + // tick whoever has the focus + if (pContext->_mouseFocus) + { + // when modal dialogs are up messages only get sent to the dialogs children. + if (IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseFocusTicked"), NULL); + } + } + // Mouse has wandered "off" the modal panel, just force a regular arrow cursor until it wanders back within the proper bounds + else if ( pContext->_appModalPanel ) + { + g_pSurface->SetCursor( vgui::dc_arrow ); + } + + //clear mouse and key states + int i; + for (i = 0; i < MOUSE_COUNT; i++) + { + pContext->_mousePressed[i] = 0; + pContext->_mouseDoublePressed[i] = 0; + pContext->_mouseReleased[i] = 0; + } + for (i = 0; i < BUTTON_CODE_COUNT; i++) + { + pContext->_keyPressed[i] = 0; + pContext->_keyTyped[i] = 0; + pContext->_keyReleased[i] = 0; + } + + VPanel *wantedKeyFocus = CalculateNewKeyFocus(); + + // make sure old and new focus get painted + if (pContext->_keyFocus != wantedKeyFocus) + { + if (pContext->_keyFocus != NULL) + { + pContext->_keyFocus->Client()->InternalFocusChanged(true); + + // there may be out of order operations here, since we're directly calling SendMessage, + // but we need to have focus messages happen immediately, since otherwise mouse events + // happen out of order - more specifically, they happen before the focus changes + + // send a message to the window saying that it's losing focus + { + MEM_ALLOC_CREDIT(); + KeyValues *pMessage = new KeyValues( "KillFocus" ); + KeyValues::AutoDelete autodelete_pMessage( pMessage ); + pMessage->SetPtr( "newPanel", wantedKeyFocus ); + pContext->_keyFocus->SendMessage( pMessage, 0 ); + } + + if ( pContext->_keyFocus ) + { + pContext->_keyFocus->Client()->Repaint(); + } + + // repaint the nearest popup as well, since it will need to redraw after losing focus + VPanel *dlg = pContext->_keyFocus; + while (dlg && !dlg->IsPopup()) + { + dlg = dlg->GetParent(); + } + if (dlg) + { + dlg->Client()->Repaint(); + } + } + if (wantedKeyFocus != NULL) + { + wantedKeyFocus->Client()->InternalFocusChanged(false); + + // there may be out of order operations here, since we're directly calling SendMessage, + // but we need to have focus messages happen immediately, since otherwise mouse events + // happen out of order - more specifically, they happen before the focus changes + + // send a message to the window saying that it's gaining focus + { + MEM_ALLOC_CREDIT(); + KeyValues *pMsg = new KeyValues("SetFocus"); + KeyValues::AutoDelete autodelete_pMsg( pMsg ); + wantedKeyFocus->SendMessage( pMsg, 0 ); + } + wantedKeyFocus->Client()->Repaint(); + + // repaint the nearest popup as well, since it will need to redraw after gaining focus + VPanel *dlg = wantedKeyFocus; + while (dlg && !dlg->IsPopup()) + { + dlg = dlg->GetParent(); + } + if (dlg) + { + dlg->Client()->Repaint(); + } + } + + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "changing kb focus from %s to %s\n", + pContext->_keyFocus ? pContext->_keyFocus->GetName() : "(no name)", + wantedKeyFocus ? wantedKeyFocus->GetName() : "(no name)" ); + } + + // accept the focus request + pContext->_keyFocus = wantedKeyFocus; + if (pContext->_keyFocus) + { + pContext->_keyFocus->MoveToFront(); + } + } + + // Pump any key repeats + KeyCode repeatCode = pContext->m_keyRepeater.KeyRepeated(); + if (repeatCode) + { + InternalKeyCodePressed( repeatCode ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Calculate the new key focus +//----------------------------------------------------------------------------- +VPanel *CInputSystem::CalculateNewKeyFocus() +{ + InputContext_t *pContext = GetInputContext(m_hContext); + + // get the top-order panel + VPanel *wantedKeyFocus = NULL; + + VPanel *pRoot = (VPanel *)pContext->_rootPanel; + VPanel *top = pRoot; + if ( g_pSurface->GetPopupCount() > 0 ) + { + // find the highest-level window that is both visible and a popup + int nIndex = g_pSurface->GetPopupCount(); + + while ( nIndex ) + { + top = (VPanel *)g_pSurface->GetPopup( --nIndex ); + + // traverse the hierarchy and check if the popup really is visible + if (top && + // top->IsPopup() && // These are right out of of the popups list!!! + top->IsVisible() && + top->IsKeyBoardInputEnabled() && + !g_pSurface->IsMinimized((VPANEL)top) && + IsChildOfModalSubTree( (VPANEL)top ) && + (!pRoot || top->HasParent( pRoot )) ) + { + bool bIsVisible = top->IsVisible(); + VPanel *p = top->GetParent(); + // drill down the hierarchy checking that everything is visible + while(p && bIsVisible) + { + if( p->IsVisible()==false) + { + bIsVisible = false; + break; + } + p=p->GetParent(); + } + + if ( bIsVisible && !g_pSurface->IsMinimized( (VPANEL)top ) ) + break; + } + + top = pRoot; + } + } + + if (top) + { + // ask the top-level panel for what it considers to be the current focus + wantedKeyFocus = (VPanel *)top->Client()->GetCurrentKeyFocus(); + if (!wantedKeyFocus) + { + wantedKeyFocus = top; + } + } + + // check to see if any of this surfaces panels have the focus + if (!g_pSurface->HasFocus()) + { + wantedKeyFocus=NULL; + } + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel((VPANEL)wantedKeyFocus)) + { + wantedKeyFocus=NULL; + } + + return wantedKeyFocus; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInputSystem::PanelDeleted(VPANEL vfocus, InputContext_t &context) +{ + VPanel *focus = (VPanel *)vfocus; + if (context._keyFocus == focus) + { + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "removing kb focus %s\n", + context._keyFocus ? context._keyFocus->GetName() : "(no name)" ); + } + context._keyFocus = NULL; + } + if (context._mouseOver == focus) + { + /* + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "removing kb focus %s\n", + context._keyFocus ? pcontext._keyFocus->GetName() : "(no name)" ); + } + */ + context._mouseOver = NULL; + } + if (context._oldMouseFocus == focus) + { + context._oldMouseFocus = NULL; + } + if (context._mouseFocus == focus) + { + context._mouseFocus = NULL; + } + + // NOTE: These two will only ever happen for the default context at the moment + if (context._mouseCapture == focus) + { + SetMouseCapture(NULL); + context._mouseCapture = NULL; + } + if (context._appModalPanel == focus) + { + ReleaseAppModalSurface(); + } + if ( context.m_pUnhandledMouseClickListener == focus ) + { + context.m_pUnhandledMouseClickListener = NULL; + } + if ( context.m_pModalSubTree == focus ) + { + context.m_pModalSubTree = NULL; + context.m_bRestrictMessagesToModalSubTree = false; + } + + context.m_KeyCodeUnhandledListeners.FindAndRemove( focus ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *focus - +//----------------------------------------------------------------------------- +void CInputSystem::PanelDeleted(VPANEL focus) +{ + HInputContext i; + for (i = m_Contexts.Head(); i != m_Contexts.InvalidIndex(); i = m_Contexts.Next(i) ) + { + PanelDeleted( focus, m_Contexts[i] ); + } + PanelDeleted( focus, m_DefaultInputContext ); +} + + + + +//----------------------------------------------------------------------------- +// Purpose: Sets the new mouse focus +// won't override _mouseCapture settings +// Input : newMouseFocus - +//----------------------------------------------------------------------------- +void CInputSystem::SetMouseFocus(VPANEL newMouseFocus) +{ + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel(newMouseFocus)) + { + return; + } + + bool wantsMouse, isPopup; // = popup->GetMouseInput(); + VPanel *panel = (VPanel *)newMouseFocus; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + wantsMouse = false; + if ( newMouseFocus ) + { + do + { + wantsMouse = panel->IsMouseInputEnabled(); + isPopup = panel->IsPopup(); + panel = panel->GetParent(); + } + while ( wantsMouse && !isPopup && panel && panel->GetParent() ); // only consider panels that want mouse input + } + + // if this panel doesn't want mouse input don't let it get focus + if (newMouseFocus && !wantsMouse) + { + return; + } + + if ((VPANEL)pContext->_mouseOver != newMouseFocus || (!pContext->_mouseCapture && (VPANEL)pContext->_mouseFocus != newMouseFocus) ) + { + pContext->_oldMouseFocus = pContext->_mouseOver; + pContext->_mouseOver = (VPanel *)newMouseFocus; + + //tell the old panel with the mouseFocus that the cursor exited + if ( pContext->_oldMouseFocus != NULL ) + { + // only notify of entry if the mouse is not captured or we're the captured panel + if ( !pContext->_mouseCapture || pContext->_oldMouseFocus == pContext->_mouseCapture ) + { + g_pIVgui->PostMessage( (VPANEL)pContext->_oldMouseFocus, new KeyValues( "CursorExited" ), NULL ); + } + } + + //tell the new panel with the mouseFocus that the cursor entered + if ( pContext->_mouseOver != NULL ) + { + // only notify of entry if the mouse is not captured or we're the captured panel + if ( !pContext->_mouseCapture || pContext->_mouseOver == pContext->_mouseCapture ) + { + g_pIVgui->PostMessage( (VPANEL)pContext->_mouseOver, new KeyValues( "CursorEntered" ), NULL ); + } + } + + // set where the mouse is currently over + // mouse capture overrides destination + VPanel *newFocus = pContext->_mouseCapture ? pContext->_mouseCapture : pContext->_mouseOver; + + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "changing mouse focus from %s to %s\n", + pContext->_mouseFocus ? pContext->_mouseFocus->GetName() : "(no name)", + newFocus ? newFocus->GetName() : "(no name)" ); + } + + + pContext->_mouseFocus = newFocus; + } +} + +VPanel *CInputSystem::GetMouseFocusIgnoringModalSubtree() +{ + // find the panel that has the focus + VPanel *focus = NULL; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + int x, y; + x = pContext->m_nCursorX; + y = pContext->m_nCursorY; + + if (!pContext->_rootPanel) + { + if (g_pSurface->IsCursorVisible() && g_pSurface->IsWithin(x, y)) + { + // faster version of code below + // checks through each popup in order, top to bottom windows + for (int i = g_pSurface->GetPopupCount() - 1; i >= 0; i--) + { + VPanel *popup = (VPanel *)g_pSurface->GetPopup(i); + VPanel *panel = popup; + bool wantsMouse = panel->IsMouseInputEnabled(); + bool isVisible = !g_pSurface->IsMinimized((VPANEL)panel); + + while ( isVisible && panel && panel->GetParent() ) // only consider panels that want mouse input + { + isVisible = panel->IsVisible(); + panel = panel->GetParent(); + } + + + if ( wantsMouse && isVisible ) + { + focus = (VPanel *)popup->Client()->IsWithinTraverse(x, y, false); + if (focus) + break; + } + } + if (!focus) + { + focus = (VPanel *)((VPanel *)g_pSurface->GetEmbeddedPanel())->Client()->IsWithinTraverse(x, y, false); + } + } + } + else + { + focus = (VPanel *)((VPanel *)(pContext->_rootPanel))->Client()->IsWithinTraverse(x, y, false); + } + + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if ( !IsChildOfModalPanel((VPANEL)focus, false )) + { + // should this be _appModalPanel? + focus = NULL; + } + + return focus; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Calculates which panel the cursor is currently over and sets it up +// as the current mouse focus. +//----------------------------------------------------------------------------- +void CInputSystem::UpdateMouseFocus(int x, int y) +{ + // find the panel that has the focus + VPanel *focus = NULL; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + if (g_pSurface->IsCursorVisible() && g_pSurface->IsWithin(x, y)) + { + // faster version of code below + // checks through each popup in order, top to bottom windows + int c = g_pSurface->GetPopupCount(); + for (int i = c - 1; i >= 0; i--) + { + VPanel *popup = (VPanel *)g_pSurface->GetPopup(i); + VPanel *panel = popup; + + if ( pContext->_rootPanel && !popup->HasParent((VPanel*)pContext->_rootPanel) ) + { + // if we have a root panel, only consider popups that belong to it + continue; + } +#if defined( _DEBUG ) + char const *pchName = popup->GetName(); + NOTE_UNUSED( pchName ); +#endif + bool wantsMouse = panel->IsMouseInputEnabled() && IsChildOfModalSubTree( (VPANEL)panel ); + if ( !wantsMouse ) + continue; + + bool isVisible = !g_pSurface->IsMinimized((VPANEL)panel); + if ( !isVisible ) + continue; + + while ( isVisible && panel && panel->GetParent() ) // only consider panels that want mouse input + { + isVisible = panel->IsVisible(); + panel = panel->GetParent(); + } + + + if ( !wantsMouse || !isVisible ) + continue; + + focus = (VPanel *)popup->Client()->IsWithinTraverse(x, y, false); + if (focus) + break; + } + if (!focus) + { + focus = (VPanel *)((VPanel *)g_pSurface->GetEmbeddedPanel())->Client()->IsWithinTraverse(x, y, false); + } + } + + // mouse focus debugging code + /* + static VPanel *oldFocus = (VPanel *)0x0001; + if (oldFocus != focus) + { + oldFocus = focus; + if (focus) + { + g_pIVgui->DPrintf2("mouse over: (%s, %s)\n", focus->GetName(), focus->GetClassName()); + } + else + { + g_pIVgui->DPrintf2("mouse over: (NULL)\n"); + } + } + */ + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel((VPANEL)focus)) + { + // should this be _appModalPanel? + focus = NULL; + } + + SetMouseFocus((VPANEL)focus); +} + +// Passes in a keycode which allows hitting other mouse buttons w/o cancelling capture mode +void CInputSystem::SetMouseCaptureEx(VPANEL panel, MouseCode captureStartMouseCode ) +{ + // This sets m_MouseCaptureStartCode to -1, so we set the real value afterward + SetMouseCapture( panel ); + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel(panel)) + { + return; + } + + InputContext_t *pContext = GetInputContext( m_hContext ); + Assert( pContext ); + pContext->m_MouseCaptureStartCode = captureStartMouseCode; +} + +VPANEL CInputSystem::GetMouseCapture() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + return (VPANEL)pContext->_mouseCapture; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets or releases the mouse capture +// Input : panel - pointer to the panel to get mouse capture +// a NULL panel means that you want to clear the mouseCapture +// MouseCaptureLost is sent to the panel that loses the mouse capture +//----------------------------------------------------------------------------- +void CInputSystem::SetMouseCapture(VPANEL panel) +{ + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel(panel)) + { + return; + } + + InputContext_t *pContext = GetInputContext( m_hContext ); + Assert( pContext ); + + pContext->m_MouseCaptureStartCode = (MouseCode)-1; + + // send a message if the panel is losing mouse capture + if (pContext->_mouseCapture && panel != (VPANEL)pContext->_mouseCapture) + { + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MouseCaptureLost"), NULL); + } + + if (panel == NULL) + { + if (pContext->_mouseCapture != NULL) + { + g_pSurface->EnableMouseCapture((VPANEL)pContext->_mouseCapture, false); + } + } + else + { + g_pSurface->EnableMouseCapture(panel, true); + } + + pContext->_mouseCapture = (VPanel *)panel; +} + +// returns true if the specified panel is a child of the current modal panel +// if no modal panel is set, then this always returns TRUE +bool CInputSystem::IsChildOfModalSubTree(VPANEL panel) +{ + if ( !panel ) + return true; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext->m_pModalSubTree ) + { + // If panel is child of modal subtree, the allow messages to route to it if restrict messages is set + bool isChildOfModal = ((VPanel *)panel)->HasParent(pContext->m_pModalSubTree ); + if ( isChildOfModal ) + { + return pContext->m_bRestrictMessagesToModalSubTree; + } + // If panel is not a child of modal subtree, then only allow messages if we're not restricting them to the modal subtree + else + { + return !pContext->m_bRestrictMessagesToModalSubTree; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: check if we are in modal state, +// and if we are make sure this panel has the modal panel as a parent +//----------------------------------------------------------------------------- +bool CInputSystem::IsChildOfModalPanel(VPANEL panel, bool checkModalSubTree /*= true*/ ) +{ + // NULL is ok. + if (!panel) + return true; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + // if we are in modal state, make sure this panel is a child of us. + if (pContext->_appModalPanel) + { + if (!((VPanel *)panel)->HasParent(pContext->_appModalPanel)) + { + return false; + } + } + + if ( !checkModalSubTree ) + return true; + + // Defer to modal subtree logic instead... + return IsChildOfModalSubTree( panel ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CInputSystem::GetFocus() +{ + return (VPANEL)( GetInputContext( m_hContext )->_keyFocus ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CInputSystem::GetCalculatedFocus() +{ + return (VPANEL) CalculateNewKeyFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CInputSystem::GetMouseOver() +{ + return (VPANEL)( GetInputContext( m_hContext )->_mouseOver ); +} + +VPANEL CInputSystem::GetMouseFocus() +{ + return (VPANEL)( GetInputContext( m_hContext )->_mouseFocus ); +} + +bool CInputSystem::WasMousePressed( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mousePressed[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::WasMouseDoublePressed( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mouseDoublePressed[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::IsMouseDown( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mouseDown[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::WasMouseReleased( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mouseReleased[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::WasKeyPressed( KeyCode code ) +{ + return GetInputContext( m_hContext )->_keyPressed[ code - KEY_FIRST ]; +} + +bool CInputSystem::IsKeyDown( KeyCode code ) +{ + return GetInputContext( m_hContext )->_keyDown[ code - KEY_FIRST ]; +} + +bool CInputSystem::WasKeyTyped( KeyCode code ) +{ + return GetInputContext( m_hContext )->_keyTyped[ code - KEY_FIRST ]; +} + +bool CInputSystem::WasKeyReleased( KeyCode code ) +{ + // changed from: only return true if the key was released and the passed in panel matches the keyFocus + return GetInputContext( m_hContext )->_keyReleased[ code - KEY_FIRST ]; +} + + +//----------------------------------------------------------------------------- +// Cursor position; this is the current position read from the input queue. +// We need to set it because client code may read this during Mouse Pressed +// events, etc. +//----------------------------------------------------------------------------- +void CInputSystem::UpdateCursorPosInternal( int x, int y ) +{ + // Windows sends a CursorMoved message even when you haven't actually + // moved the cursor, this means we are going into this fxn just by clicking + // in the window. We only want to execute this code if we have actually moved + // the cursor while dragging. So this code has been added to check + // if we have actually moved from our previous position. + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext->m_nCursorX == x && pContext->m_nCursorY == y ) + return; + + pContext->m_nCursorX = x; + pContext->m_nCursorY = y; + + // Cursor has moved, so make sure the mouseFocus is current + UpdateMouseFocus( x, y ); +} + + +//----------------------------------------------------------------------------- +// This is called by panels to teleport the cursor +//----------------------------------------------------------------------------- +void CInputSystem::SetCursorPos( int x, int y ) +{ + if ( IsDispatchingMessageQueue() ) + { + InputContext_t *pContext = GetInputContext( m_hContext ); + pContext->m_nExternallySetCursorX = x; + pContext->m_nExternallySetCursorY = y; + pContext->m_bSetCursorExplicitly = true; + } + else + { + SurfaceSetCursorPos( x, y ); + } +} + + +void CInputSystem::GetCursorPos(int &x, int &y) +{ + if ( IsDispatchingMessageQueue() ) + { + GetCursorPosition( x, y ); + } + else + { + SurfaceGetCursorPos( x, y ); + } +} + + +// Here for backward compat +void CInputSystem::GetCursorPosition( int &x, int &y ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + x = pContext->m_nCursorX; + y = pContext->m_nCursorY; +} + +//----------------------------------------------------------------------------- +// Purpose: Converts a key code into a full key name +//----------------------------------------------------------------------------- +void CInputSystem::GetKeyCodeText(KeyCode code, char *buf, int buflen) +{ + if (!buf) + return; + + // copy text into buf up to buflen in length + // skip 2 in _keyTrans because the first two are for GetKeyCodeChar + for (int i = 0; i < buflen; i++) + { + char ch = _keyTrans[code][i+2]; + buf[i] = ch; + if (ch == 0) + break; + } + +} + + +//----------------------------------------------------------------------------- +// Low-level cursor getting/setting functions +//----------------------------------------------------------------------------- +void CInputSystem::SurfaceSetCursorPos(int x, int y) +{ + if ( g_pSurface->HasCursorPosFunctions() ) // does the surface export cursor functions for us to use? + { + g_pSurface->SurfaceSetCursorPos(x,y); + } + else + { + // translate into coordinates relative to surface + int px, py, pw, pt; + g_pSurface->GetAbsoluteWindowBounds(px, py, pw, pt); + x += px; + y += py; + // set windows cursor pos +#ifdef WIN32 + ::SetCursorPos(x, y); +#else + // From Alfred on 8/15/2012. + // For l4d2, the vguimatsurface/cursor.cpp functions fire in the engine, the vgui2 ones + // should be dormant (this isn't true for Steam however). + // + // If we ever do need to implement this, look at SDL_GetMouseState(), etc. + Assert( !"CInputSystem::SurfaceSetCursorPos NYI" ); +#endif + } +} + +void CInputSystem::SurfaceGetCursorPos( int &x, int &y ) +{ +#ifndef _X360 // X360TBD + if ( g_pSurface->HasCursorPosFunctions() ) // does the surface export cursor functions for us to use? + { + g_pSurface->SurfaceGetCursorPos( x,y ); + } + else + { +#ifdef WIN32 + // get mouse position in windows + POINT pnt; + VCRHook_GetCursorPos(&pnt); + x = pnt.x; + y = pnt.y; + + // translate into coordinates relative to surface + int px, py, pw, pt; + g_pSurface->GetAbsoluteWindowBounds(px, py, pw, pt); + x -= px; + y -= py; +#else + // From Alfred on 8/15/2012. + // For l4d2, the vguimatsurface/cursor.cpp functions fire in the engine, the vgui2 ones + // should be dormant (this isn't true for Steam however). + Assert( !"CInputSystem::SurfaceGetCursorPos NYI" ); + x = 0; + y = 0; +#endif + } +#else + x = 0; + y = 0; +#endif +} + +void CInputSystem::SetCursorOveride(HCursor cursor) +{ + _cursorOverride = cursor; +} + +HCursor CInputSystem::GetCursorOveride() +{ + return _cursorOverride; +} + + +//----------------------------------------------------------------------------- +// Called when we've detected cursor has moved via a windows message +//----------------------------------------------------------------------------- +bool CInputSystem::InternalCursorMoved(int x, int y) +{ + g_pIVgui->PostMessage((VPANEL) MESSAGE_CURSOR_POS, new KeyValues("SetCursorPosInternal", "xpos", x, "ypos", y), NULL); + return true; +} + + +//----------------------------------------------------------------------------- +// Makes sure the windows cursor is in the right place after processing input +//----------------------------------------------------------------------------- +void CInputSystem::HandleExplicitSetCursor( ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + if ( pContext->m_bSetCursorExplicitly ) + { + pContext->m_nCursorX = pContext->m_nExternallySetCursorX; + pContext->m_nCursorY = pContext->m_nExternallySetCursorY; + pContext->m_bSetCursorExplicitly = false; + + // NOTE: This forces a cursor moved message to be posted next time + pContext->m_nLastPostedCursorX = pContext->m_nLastPostedCursorY = -9999; + + SurfaceSetCursorPos( pContext->m_nCursorX, pContext->m_nCursorY ); + UpdateMouseFocus( pContext->m_nCursorX, pContext->m_nCursorY ); + } +} + + +//----------------------------------------------------------------------------- +// Called when we've detected cursor has moved via a windows message +//----------------------------------------------------------------------------- +void CInputSystem::PostCursorMessage( ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + if ( pContext->m_bSetCursorExplicitly ) + { + // NOTE m_bSetCursorExplicitly will be reset to false in HandleExplicitSetCursor + pContext->m_nCursorX = pContext->m_nExternallySetCursorX; + pContext->m_nCursorY = pContext->m_nExternallySetCursorY; + } + + if ( pContext->m_nLastPostedCursorX == pContext->m_nCursorX && pContext->m_nLastPostedCursorY == pContext->m_nCursorY ) + return; + + pContext->m_nLastPostedCursorX = pContext->m_nCursorX; + pContext->m_nLastPostedCursorY = pContext->m_nCursorY; + + if ( pContext->_mouseCapture ) + { + if (!IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + return; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("CursorMoved", "xpos", pContext->m_nCursorX, "ypos", pContext->m_nCursorY), NULL); + } + else if (pContext->_mouseFocus != NULL) + { + // mouse focus is current from UpdateMouse focus + // so the appmodal check has already been made. + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("CursorMoved", "xpos", pContext->m_nCursorX, "ypos", pContext->m_nCursorY), NULL); + } +} + +bool CInputSystem::InternalMousePressed(MouseCode code) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + VPanel *pTargetPanel = pContext->_mouseOver; + if ( pContext->_mouseCapture && IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + bFilter = true; + + bool captureLost = code == pContext->m_MouseCaptureStartCode || pContext->m_MouseCaptureStartCode == (MouseCode)-1; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MousePressed", "code", code), NULL); + pTargetPanel = pContext->_mouseCapture; + + if ( captureLost ) + { + // this has to happen after MousePressed so the panel doesn't Think it got a mouse press after it lost capture + SetMouseCapture(NULL); + } + } + else if ( (pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus) ) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + bFilter = true; + + // tell the panel with the mouseFocus that the mouse was presssed + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MousePressed", "code", code), NULL); +// g_pIVgui->DPrintf2("MousePressed: (%s, %s)\n", _mouseFocus->GetName(), _mouseFocus->GetClassName()); + pTargetPanel = pContext->_mouseFocus; + } + else if ( pContext->m_pModalSubTree && pContext->m_pUnhandledMouseClickListener ) + { + VPanel *p = GetMouseFocusIgnoringModalSubtree(); + if ( p ) + { + bool isChildOfModal = IsChildOfModalSubTree( (VPANEL)p ); + bool isUnRestricted = !pContext->m_bRestrictMessagesToModalSubTree; + + if ( isUnRestricted != isChildOfModal ) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + g_pIVgui->PostMessage( ( VPANEL )pContext->m_pUnhandledMouseClickListener, new KeyValues( "UnhandledMouseClick", "code", code ), NULL ); + pTargetPanel = pContext->m_pUnhandledMouseClickListener; + bFilter = true; + } + } + } + + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if ( IsChildOfModalPanel( (VPANEL)pTargetPanel ) ) + { + g_pSurface->SetTopLevelFocus( (VPANEL)pTargetPanel ); + } + + return bFilter; +} + +bool CInputSystem::InternalMouseDoublePressed(MouseCode code) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + VPanel *pTargetPanel = pContext->_mouseOver; + if ( pContext->_mouseCapture && IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MouseDoublePressed", "code", code), NULL); + pTargetPanel = pContext->_mouseCapture; + bFilter = true; + } + else if ( (pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + // tell the panel with the mouseFocus that the mouse was double presssed + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseDoublePressed", "code", code), NULL); + pTargetPanel = pContext->_mouseFocus; + bFilter = true; + } + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (IsChildOfModalPanel((VPANEL)pTargetPanel)) + { + g_pSurface->SetTopLevelFocus((VPANEL)pTargetPanel); + } + + return bFilter; +} + +bool CInputSystem::InternalMouseReleased( MouseCode code ) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if (pContext->_mouseCapture && IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MouseReleased", "code", code), NULL ); + bFilter = true; + } + else if ((pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + //tell the panel with the mouseFocus that the mouse was release + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseReleased", "code", code), NULL ); + bFilter = true; + } + + return bFilter; +} + +bool CInputSystem::InternalMouseWheeled(int delta) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if ((pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + // the mouseWheel works with the mouseFocus, not the keyFocus + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseWheeled", "delta", delta), NULL); + bFilter = true; + } + return bFilter; +} + +//----------------------------------------------------------------------------- +// Updates the internal key/mouse state associated with the current input context without sending messages +//----------------------------------------------------------------------------- +void CInputSystem::SetMouseCodeState( MouseCode code, MouseCodeState_t state ) +{ + if ( !IsMouseCode( code ) ) + return; + + InputContext_t *pContext = GetInputContext( m_hContext ); + switch( state ) + { + case BUTTON_RELEASED: + pContext->_mouseReleased[ code - MOUSE_FIRST ] = 1; + break; + + case BUTTON_PRESSED: + pContext->_mousePressed[ code - MOUSE_FIRST ] = 1; + break; + + case BUTTON_DOUBLECLICKED: + pContext->_mouseDoublePressed[ code - MOUSE_FIRST ] = 1; + break; + } + + pContext->_mouseDown[ code - MOUSE_FIRST ] = ( state != BUTTON_RELEASED ); +} + +void CInputSystem::SetKeyCodeState( KeyCode code, bool bPressed ) +{ + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( bPressed ) + { + //set key state + pContext->_keyPressed[ code - KEY_FIRST ] = 1; + } + else + { + // set key state + pContext->_keyReleased[ code - KEY_FIRST ] = 1; + } + pContext->_keyDown[ code - KEY_FIRST ] = bPressed; +} + +void CInputSystem::UpdateButtonState( const InputEvent_t &event ) +{ + switch( event.m_nType ) + { + case IE_ButtonPressed: + case IE_ButtonReleased: + case IE_ButtonDoubleClicked: + { + // NOTE: data2 is the virtual key code (data1 contains the scan-code one) + ButtonCode_t code = (ButtonCode_t)event.m_nData2; + + // FIXME: Workaround hack + if ( IsKeyCode( code ) || IsJoystickCode( code ) ) + { + SetKeyCodeState( code, ( event.m_nType != IE_ButtonReleased ) ); + break; + } + + if ( IsMouseCode( code ) ) + { + MouseCodeState_t state; + state = ( event.m_nType == IE_ButtonReleased ) ? vgui::BUTTON_RELEASED : vgui::BUTTON_PRESSED; + if ( event.m_nType == IE_ButtonDoubleClicked ) + { + state = vgui::BUTTON_DOUBLECLICKED; + } + + SetMouseCodeState( code, state ); + break; + } + } + break; + } +} + +bool CInputSystem::InternalKeyCodePressed( KeyCode code ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + // mask out bogus keys + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return false; + + bool bFilter = PostKeyMessage( new KeyValues("KeyCodePressed", "code", code ) ); + if ( bFilter ) + { + // Only notice the key down for repeating if we actually used the key + pContext->m_keyRepeater.KeyDown( code ); + } + return bFilter; +} + +void CInputSystem::InternalKeyCodeTyped( KeyCode code ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + // mask out bogus keys + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return; + + // set key state + pContext->_keyTyped[ code - KEY_FIRST ] = 1; + + // tell the current focused panel that a key was typed + PostKeyMessage(new KeyValues("KeyCodeTyped", "code", code)); +} + +void CInputSystem::InternalKeyTyped(wchar_t unichar) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + // set key state + if( unichar <= KEY_LAST ) + { + pContext->_keyTyped[unichar]=1; + } + + // tell the current focused panel that a key was typed + PostKeyMessage(new KeyValues("KeyTyped", "unichar", unichar)); +} + +bool CInputSystem::InternalKeyCodeReleased( KeyCode code ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + // mask out bogus keys + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return false; + + pContext->m_keyRepeater.KeyUp( code ); + + return PostKeyMessage(new KeyValues("KeyCodeReleased", "code", code)); +} + +//----------------------------------------------------------------------------- +// Purpose: posts a message to the key focus if it's valid +//----------------------------------------------------------------------------- +bool CInputSystem::PostKeyMessage(KeyValues *message) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if( (pContext->_keyFocus!= NULL) && IsChildOfModalPanel((VPANEL)pContext->_keyFocus)) + { +#ifdef _X360 + g_pIVgui->PostMessage((VPANEL) MESSAGE_CURRENT_KEYFOCUS, message, NULL ); +#else + //tell the current focused panel that a key was released + g_pIVgui->PostMessage((VPANEL)pContext->_keyFocus, message, NULL ); +#endif + return true; + } + + message->deleteThis(); + return false; +} + +VPANEL CInputSystem::GetAppModalSurface() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + return (VPANEL)pContext->_appModalPanel; +} + +void CInputSystem::SetAppModalSurface(VPANEL panel) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + pContext->_appModalPanel = (VPanel *)panel; +} + + +void CInputSystem::ReleaseAppModalSurface() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + pContext->_appModalPanel = NULL; +} + + +#ifdef DO_IME + +enum LANGFLAG +{ + ENGLISH, + TRADITIONAL_CHINESE, + JAPANESE, + KOREAN, + SIMPLIFIED_CHINESE, + UNKNOWN, + + NUM_IMES_SUPPORTED +} LangFlag; + +struct LanguageIds +{ + // char const *idname; + unsigned short id; + int languageflag; + wchar_t const *shortcode; + wchar_t const *displayname; + bool invertcomposition; +}; + +LanguageIds g_LanguageIds[] = +{ + { 0x0000, UNKNOWN, L"", L"Neutral" }, + { 0x007f, UNKNOWN, L"", L"Invariant" }, + { 0x0400, UNKNOWN, L"", L"User Default Language" }, + { 0x0800, UNKNOWN, L"", L"System Default Language" }, + { 0x0436, UNKNOWN, L"AF", L"Afrikaans" }, + { 0x041c, UNKNOWN, L"SQ", L"Albanian" }, + { 0x0401, UNKNOWN, L"AR", L"Arabic (Saudi Arabia)" }, + { 0x0801, UNKNOWN, L"AR", L"Arabic (Iraq)" }, + { 0x0c01, UNKNOWN, L"AR", L"Arabic (Egypt)" }, + { 0x1001, UNKNOWN, L"AR", L"Arabic (Libya)" }, + { 0x1401, UNKNOWN, L"AR", L"Arabic (Algeria)" }, + { 0x1801, UNKNOWN, L"AR", L"Arabic (Morocco)" }, + { 0x1c01, UNKNOWN, L"AR", L"Arabic (Tunisia)" }, + { 0x2001, UNKNOWN, L"AR", L"Arabic (Oman)" }, + { 0x2401, UNKNOWN, L"AR", L"Arabic (Yemen)" }, + { 0x2801, UNKNOWN, L"AR", L"Arabic (Syria)" }, + { 0x2c01, UNKNOWN, L"AR", L"Arabic (Jordan)" }, + { 0x3001, UNKNOWN, L"AR", L"Arabic (Lebanon)" }, + { 0x3401, UNKNOWN, L"AR", L"Arabic (Kuwait)" }, + { 0x3801, UNKNOWN, L"AR", L"Arabic (U.A.E.)" }, + { 0x3c01, UNKNOWN, L"AR", L"Arabic (Bahrain)" }, + { 0x4001, UNKNOWN, L"AR", L"Arabic (Qatar)" }, + { 0x042b, UNKNOWN, L"HY", L"Armenian" }, + { 0x042c, UNKNOWN, L"AZ", L"Azeri (Latin)" }, + { 0x082c, UNKNOWN, L"AZ", L"Azeri (Cyrillic)" }, + { 0x042d, UNKNOWN, L"ES", L"Basque" }, + { 0x0423, UNKNOWN, L"BE", L"Belarusian" }, + { 0x0445, UNKNOWN, L"", L"Bengali (India)" }, + { 0x141a, UNKNOWN, L"", L"Bosnian (Bosnia and Herzegovina)" }, + { 0x0402, UNKNOWN, L"BG", L"Bulgarian" }, + { 0x0455, UNKNOWN, L"", L"Burmese" }, + { 0x0403, UNKNOWN, L"CA", L"Catalan" }, + { 0x0404, TRADITIONAL_CHINESE, L"CHT", L"#IME_0404", true }, + { 0x0804, SIMPLIFIED_CHINESE, L"CHS", L"#IME_0804", true }, + { 0x0c04, UNKNOWN, L"CH", L"Chinese (Hong Kong SAR, PRC)" }, + { 0x1004, UNKNOWN, L"CH", L"Chinese (Singapore)" }, + { 0x1404, UNKNOWN, L"CH", L"Chinese (Macao SAR)" }, + { 0x041a, UNKNOWN, L"HR", L"Croatian" }, + { 0x101a, UNKNOWN, L"HR", L"Croatian (Bosnia and Herzegovina)" }, + { 0x0405, UNKNOWN, L"CZ", L"Czech" }, + { 0x0406, UNKNOWN, L"DK", L"Danish" }, + { 0x0465, UNKNOWN, L"MV", L"Divehi" }, + { 0x0413, UNKNOWN, L"NL", L"Dutch (Netherlands)" }, + { 0x0813, UNKNOWN, L"BE", L"Dutch (Belgium)" }, + { 0x0409, ENGLISH, L"EN", L"#IME_0409" }, + { 0x0809, ENGLISH, L"EN", L"English (United Kingdom)" }, + { 0x0c09, ENGLISH, L"EN", L"English (Australian)" }, + { 0x1009, ENGLISH, L"EN", L"English (Canadian)" }, + { 0x1409, ENGLISH, L"EN", L"English (New Zealand)" }, + { 0x1809, ENGLISH, L"EN", L"English (Ireland)" }, + { 0x1c09, ENGLISH, L"EN", L"English (South Africa)" }, + { 0x2009, ENGLISH, L"EN", L"English (Jamaica)" }, + { 0x2409, ENGLISH, L"EN", L"English (Caribbean)" }, + { 0x2809, ENGLISH, L"EN", L"English (Belize)" }, + { 0x2c09, ENGLISH, L"EN", L"English (Trinidad)" }, + { 0x3009, ENGLISH, L"EN", L"English (Zimbabwe)" }, + { 0x3409, ENGLISH, L"EN", L"English (Philippines)" }, + { 0x0425, UNKNOWN, L"ET", L"Estonian" }, + { 0x0438, UNKNOWN, L"FO", L"Faeroese" }, + { 0x0429, UNKNOWN, L"FA", L"Farsi" }, + { 0x040b, UNKNOWN, L"FI", L"Finnish" }, + { 0x040c, UNKNOWN, L"FR", L"#IME_040c" }, + { 0x080c, UNKNOWN, L"FR", L"French (Belgian)" }, + { 0x0c0c, UNKNOWN, L"FR", L"French (Canadian)" }, + { 0x100c, UNKNOWN, L"FR", L"French (Switzerland)" }, + { 0x140c, UNKNOWN, L"FR", L"French (Luxembourg)" }, + { 0x180c, UNKNOWN, L"FR", L"French (Monaco)" }, + { 0x0456, UNKNOWN, L"GL", L"Galician" }, + { 0x0437, UNKNOWN, L"KA", L"Georgian" }, + { 0x0407, UNKNOWN, L"DE", L"#IME_0407" }, + { 0x0807, UNKNOWN, L"DE", L"German (Switzerland)" }, + { 0x0c07, UNKNOWN, L"DE", L"German (Austria)" }, + { 0x1007, UNKNOWN, L"DE", L"German (Luxembourg)" }, + { 0x1407, UNKNOWN, L"DE", L"German (Liechtenstein)" }, + { 0x0408, UNKNOWN, L"GR", L"Greek" }, + { 0x0447, UNKNOWN, L"IN", L"Gujarati" }, + { 0x040d, UNKNOWN, L"HE", L"Hebrew" }, + { 0x0439, UNKNOWN, L"HI", L"Hindi" }, + { 0x040e, UNKNOWN, L"HU", L"Hungarian" }, + { 0x040f, UNKNOWN, L"IS", L"Icelandic" }, + { 0x0421, UNKNOWN, L"ID", L"Indonesian" }, + { 0x0434, UNKNOWN, L"", L"isiXhosa/Xhosa (South Africa)" }, + { 0x0435, UNKNOWN, L"", L"isiZulu/Zulu (South Africa)" }, + { 0x0410, UNKNOWN, L"IT", L"#IME_0410" }, + { 0x0810, UNKNOWN, L"IT", L"Italian (Switzerland)" }, + { 0x0411, JAPANESE, L"JP", L"#IME_0411" }, + { 0x044b, UNKNOWN, L"IN", L"Kannada" }, + { 0x0457, UNKNOWN, L"IN", L"Konkani" }, + { 0x0412, KOREAN, L"KR", L"#IME_0412" }, + { 0x0812, UNKNOWN, L"KR", L"Korean (Johab)" }, + { 0x0440, UNKNOWN, L"KZ", L"Kyrgyz." }, + { 0x0426, UNKNOWN, L"LV", L"Latvian" }, + { 0x0427, UNKNOWN, L"LT", L"Lithuanian" }, + { 0x0827, UNKNOWN, L"LT", L"Lithuanian (Classic)" }, + { 0x042f, UNKNOWN, L"MK", L"FYRO Macedonian" }, + { 0x043e, UNKNOWN, L"MY", L"Malay (Malaysian)" }, + { 0x083e, UNKNOWN, L"MY", L"Malay (Brunei Darussalam)" }, + { 0x044c, UNKNOWN, L"IN", L"Malayalam (India)" }, + { 0x0481, UNKNOWN, L"", L"Maori (New Zealand)" }, + { 0x043a, UNKNOWN, L"", L"Maltese (Malta)" }, + { 0x044e, UNKNOWN, L"IN", L"Marathi" }, + { 0x0450, UNKNOWN, L"MN", L"Mongolian" }, + { 0x0414, UNKNOWN, L"NO", L"Norwegian (Bokmal)" }, + { 0x0814, UNKNOWN, L"NO", L"Norwegian (Nynorsk)" }, + { 0x0415, UNKNOWN, L"PL", L"Polish" }, + { 0x0416, UNKNOWN, L"PT", L"Portuguese (Brazil)" }, + { 0x0816, UNKNOWN, L"PT", L"Portuguese (Portugal)" }, + { 0x0446, UNKNOWN, L"IN", L"Punjabi" }, + { 0x046b, UNKNOWN, L"", L"Quechua (Bolivia)" }, + { 0x086b, UNKNOWN, L"", L"Quechua (Ecuador)" }, + { 0x0c6b, UNKNOWN, L"", L"Quechua (Peru)" }, + { 0x0418, UNKNOWN, L"RO", L"Romanian" }, + { 0x0419, UNKNOWN, L"RU", L"#IME_0419" }, + { 0x044f, UNKNOWN, L"IN", L"Sanskrit" }, + { 0x043b, UNKNOWN, L"", L"Sami, Northern (Norway)" }, + { 0x083b, UNKNOWN, L"", L"Sami, Northern (Sweden)" }, + { 0x0c3b, UNKNOWN, L"", L"Sami, Northern (Finland)" }, + { 0x103b, UNKNOWN, L"", L"Sami, Lule (Norway)" }, + { 0x143b, UNKNOWN, L"", L"Sami, Lule (Sweden)" }, + { 0x183b, UNKNOWN, L"", L"Sami, Southern (Norway)" }, + { 0x1c3b, UNKNOWN, L"", L"Sami, Southern (Sweden)" }, + { 0x203b, UNKNOWN, L"", L"Sami, Skolt (Finland)" }, + { 0x243b, UNKNOWN, L"", L"Sami, Inari (Finland)" }, + { 0x0c1a, UNKNOWN, L"SR", L"Serbian (Cyrillic)" }, + { 0x1c1a, UNKNOWN, L"SR", L"Serbian (Cyrillic, Bosnia, and Herzegovina)" }, + { 0x081a, UNKNOWN, L"SR", L"Serbian (Latin)" }, + { 0x181a, UNKNOWN, L"SR", L"Serbian (Latin, Bosnia, and Herzegovina)" }, + { 0x046c, UNKNOWN, L"", L"Sesotho sa Leboa/Northern Sotho (South Africa)" }, + { 0x0432, UNKNOWN, L"", L"Setswana/Tswana (South Africa)" }, + { 0x041b, UNKNOWN, L"SK", L"Slovak" }, + { 0x0424, UNKNOWN, L"SI", L"Slovenian" }, + { 0x040a, UNKNOWN, L"ES", L"#IME_040a" }, + { 0x080a, UNKNOWN, L"ES", L"Spanish (Mexican)" }, + { 0x0c0a, UNKNOWN, L"ES", L"Spanish (Spain, Modern Sort)" }, + { 0x100a, UNKNOWN, L"ES", L"Spanish (Guatemala)" }, + { 0x140a, UNKNOWN, L"ES", L"Spanish (Costa Rica)" }, + { 0x180a, UNKNOWN, L"ES", L"Spanish (Panama)" }, + { 0x1c0a, UNKNOWN, L"ES", L"Spanish (Dominican Republic)" }, + { 0x200a, UNKNOWN, L"ES", L"Spanish (Venezuela)" }, + { 0x240a, UNKNOWN, L"ES", L"Spanish (Colombia)" }, + { 0x280a, UNKNOWN, L"ES", L"Spanish (Peru)" }, + { 0x2c0a, UNKNOWN, L"ES", L"Spanish (Argentina)" }, + { 0x300a, UNKNOWN, L"ES", L"Spanish (Ecuador)" }, + { 0x340a, UNKNOWN, L"ES", L"Spanish (Chile)" }, + { 0x380a, UNKNOWN, L"ES", L"Spanish (Uruguay)" }, + { 0x3c0a, UNKNOWN, L"ES", L"Spanish (Paraguay)" }, + { 0x400a, UNKNOWN, L"ES", L"Spanish (Bolivia)" }, + { 0x440a, UNKNOWN, L"ES", L"Spanish (El Salvador)" }, + { 0x480a, UNKNOWN, L"ES", L"Spanish (Honduras)" }, + { 0x4c0a, UNKNOWN, L"ES", L"Spanish (Nicaragua)" }, + { 0x500a, UNKNOWN, L"ES", L"Spanish (Puerto Rico)" }, + { 0x0430, UNKNOWN, L"", L"Sutu" }, + { 0x0441, UNKNOWN, L"KE", L"Swahili (Kenya)" }, + { 0x041d, UNKNOWN, L"SV", L"Swedish" }, + { 0x081d, UNKNOWN, L"SV", L"Swedish (Finland)" }, + { 0x045a, UNKNOWN, L"SY", L"Syriac" }, + { 0x0449, UNKNOWN, L"IN", L"Tamil" }, + { 0x0444, UNKNOWN, L"RU", L"Tatar (Tatarstan)" }, + { 0x044a, UNKNOWN, L"IN", L"Telugu" }, + { 0x041e, UNKNOWN, L"TH", L"#IME_041e" }, + { 0x041f, UNKNOWN, L"TR", L"Turkish" }, + { 0x0422, UNKNOWN, L"UA", L"Ukrainian" }, + { 0x0420, UNKNOWN, L"PK", L"Urdu (Pakistan)" }, + { 0x0820, UNKNOWN, L"IN", L"Urdu (India)" }, + { 0x0443, UNKNOWN, L"UZ", L"Uzbek (Latin)" }, + { 0x0843, UNKNOWN, L"UZ", L"Uzbek (Cyrillic)" }, + { 0x042a, UNKNOWN, L"VN", L"Vietnamese" }, + { 0x0452, UNKNOWN, L"", L"Welsh (United Kingdom)" }, +}; + +static LanguageIds *GetLanguageInfo( unsigned short id ) +{ + for ( int j = 0; j < sizeof( g_LanguageIds ) / sizeof( g_LanguageIds[ 0 ] ); ++j ) + { + if ( g_LanguageIds[ j ].id == id ) + { + return &g_LanguageIds[ j ]; + break; + } + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// CIMEDlg message handlers +static bool IsIDInList( unsigned short id, int count, HKL *list ) +{ + for ( int i = 0; i < count; ++i ) + { + if ( LOWORD( list[ i ] ) == id ) + { + return true; + } + } + return false; +} + +static const wchar_t *GetLanguageName( unsigned short id ) +{ + wchar_t const *name = L"???"; + for ( int j = 0; j < sizeof( g_LanguageIds ) / sizeof( g_LanguageIds[ 0 ] ); ++j ) + { + if ( g_LanguageIds[ j ].id == id ) + { + name = g_LanguageIds[ j ].displayname; + break; + } + } + return name; +} + +#endif // DO_IME + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *hwnd - +//----------------------------------------------------------------------------- +void CInputSystem::SetIMEWindow( void *hwnd ) +{ +#ifdef DO_IME + _imeWnd = hwnd; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void *CInputSystem::GetIMEWindow() +{ +#ifdef DO_IME + return _imeWnd; +#else + return NULL; +#endif +} + +#ifdef DO_IME +static void SpewIMEInfo( int langid ) +{ + LanguageIds *info = GetLanguageInfo( langid ); + if ( info ) + { + wchar_t const *name = info->shortcode ? info->shortcode : L"???"; + wchar_t outstr[ 512 ]; + V_swprintf_safe( outstr, L"IME language changed to: %s", name ); + OutputDebugStringW( outstr ); + OutputDebugStringW( L"\n" ); + } +} +#endif // DO_IME + +// Change keyboard layout type +void CInputSystem::OnChangeIME( bool forward ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HKL currentKb = GetKeyboardLayout( 0 ); + + UINT numKBs = GetKeyboardLayoutList( 0, NULL ); + if ( numKBs > 0 ) + { + HKL *list = new HKL[ numKBs ]; + + GetKeyboardLayoutList( numKBs, list ); + + int oldKb = 0; + CUtlVector< HKL > selections; + + for ( unsigned int i = 0; i < numKBs; ++i ) + { + BOOL first = !IsIDInList( LOWORD( list[ i ] ), i, list ); + + if ( !first ) + continue; + + selections.AddToTail( list[ i ] ); + if ( list[ i ] == currentKb ) + { + oldKb = selections.Count() - 1; + } + } + + oldKb += forward ? 1 : -1; + if ( oldKb < 0 ) + { + oldKb = max( 0, selections.Count() - 1 ); + } + else if ( oldKb >= selections.Count() ) + { + oldKb = 0; + } + + ActivateKeyboardLayout( selections[ oldKb ], 0 ); + + int langid = LOWORD( selections[ oldKb ] ); + SpewIMEInfo( langid ); + + delete[] list; + } +#endif +} + +int CInputSystem::GetCurrentIMEHandle() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HKL hkl = (HKL)GetKeyboardLayout( 0 ); + return (int)hkl; +#else + return 0; +#endif +} + +int CInputSystem::GetEnglishIMEHandle() +{ +#ifdef DO_IME + HKL hkl = (HKL)0x04090409; + return (int)hkl; +#else + return 0; +#endif +} + +void CInputSystem::OnChangeIMEByHandle( int handleValue ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HKL hkl = (HKL)handleValue; + + ActivateKeyboardLayout( hkl, 0 ); + + int langid = LOWORD( hkl); + + SpewIMEInfo( langid ); +#endif +} + + // Returns the Language Bar label (Chinese, Korean, Japanese, Russion, Thai, etc.) +void CInputSystem::GetIMELanguageName( wchar_t *buf, int unicodeBufferSizeInBytes ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + wchar_t const *name = GetLanguageName( LOWORD( GetKeyboardLayout( 0 ) ) ); + wcsncpy( buf, name, unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ); + buf[ unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ] = L'\0'; +#else + buf[0] = L'\0'; +#endif +} + // Returns the short code for the language (EN, CH, KO, JP, RU, TH, etc. ). +void CInputSystem::GetIMELanguageShortCode( wchar_t *buf, int unicodeBufferSizeInBytes ) +{ +#ifdef DO_IME + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + if ( !info ) + { + buf[ 0 ] = L'\0'; + } + else + { + wcsncpy( buf, info->shortcode, unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ); + buf[ unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ] = L'\0'; + } +#else + buf[0] = L'\0'; +#endif +} + +// Call with NULL dest to get item count +int CInputSystem::GetIMELanguageList( LanguageItem *dest, int destcount ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + int iret = 0; + + UINT numKBs = GetKeyboardLayoutList( 0, NULL ); + if ( numKBs > 0 ) + { + HKL *list = new HKL[ numKBs ]; + + GetKeyboardLayoutList( numKBs, list ); + + CUtlVector< HKL > selections; + + for ( unsigned int i = 0; i < numKBs; ++i ) + { + BOOL first = !IsIDInList( LOWORD( list[ i ] ), i, list ); + + if ( !first ) + continue; + + selections.AddToTail( list[ i ] ); + } + + iret = selections.Count(); + if ( dest ) + { + for ( int i = 0; i < min(iret,destcount); ++i ) + { + HKL hkl = selections[ i ]; + + IInput::LanguageItem *p = &dest[ i ]; + + LanguageIds *info = GetLanguageInfo( LOWORD( hkl ) ); + + memset( p, 0, sizeof( IInput::LanguageItem ) ); + + wcsncpy( p->shortname, info->shortcode, sizeof( p->shortname ) / sizeof( wchar_t ) ); + p->shortname[ sizeof( p->shortname ) / sizeof( wchar_t ) - 1 ] = L'\0'; + + wcsncpy( p->menuname, info->displayname, sizeof( p->menuname ) / sizeof( wchar_t ) ); + p->menuname[ sizeof( p->menuname ) / sizeof( wchar_t ) - 1 ] = L'\0'; + + p->handleValue = (int)hkl; + p->active = ( hkl == GetKeyboardLayout( 0 ) ) ? true : false; + } + } + + delete[] list; + } + return iret; +#else + return 0; +#endif +} + +/* +// Flag for effective options in conversion mode +BOOL fConvMode[NUM_IMES_SUPPORTED][13] = +{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // EN + {1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0}, // Trad CH + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}, // Japanese + {1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // Kor + {1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0}, // Simp CH + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // UNK(same as EN) +} + +// Flag for effective options in sentence mode +BOOL fSentMode[NUM_IMES_SUPPORTED][6] = +{ + {0, 0, 0, 0, 0, 0}, // EN + {0, 1, 0, 0, 0, 0}, // Trad CH + {1, 1, 1, 1, 1, 1}, // Japanese + {0, 0, 0, 0, 0, 0}, // Kor + {0, 0, 0, 0, 0, 0} // Simp CH + {0, 0, 0, 0, 0, 0}, // UNK(same as EN) +}; + +// Conversion mode message +DWORD dwConvModeMsg[13] = { + IME_CMODE_ALPHANUMERIC, IME_CMODE_NATIVE, IME_CMODE_KATAKANA, + IME_CMODE_LANGUAGE, IME_CMODE_FULLSHAPE, IME_CMODE_ROMAN, + IME_CMODE_CHARCODE, IME_CMODE_HANJACONVERT, IME_CMODE_SOFTKBD, + IME_CMODE_NOCONVERSION, IME_CMODE_EUDC, IME_CMODE_SYMBOL, + IME_CMODE_FIXED}; + +// Sentence mode message +DWORD dwSentModeMsg[6] = { + IME_SMODE_NONE, IME_SMODE_PLAURALCLAUSE, IME_SMODE_SINGLECONVERT, + IME_SMODE_AUTOMATIC, IME_SMODE_PHRASEPREDICT, IME_SMODE_CONVERSATION }; + +// ENGLISH, +// TRADITIONAL_CHINESE, +// JAPANESE, +// KOREAN, +// SIMPLIFIED_CHINESE, +// UNKNOWN, +*/ + +#ifdef DO_IME + +struct IMESettingsTransform +{ + IMESettingsTransform( unsigned int cmr, unsigned int cma, unsigned int smr, unsigned int sma ) : + cmode_remove( cmr ), + cmode_add( cma ), + smode_remove( smr ), + smode_add( sma ) + { + } + + void Apply( HWND hwnd ) + { + HIMC hImc = ImmGetContext( hwnd ); + if ( hImc ) + { + DWORD dwConvMode, dwSentMode; + + ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode ); + + dwConvMode &= ~cmode_remove; + dwSentMode &= ~smode_remove; + + ImmSetConversionStatus( hImc, dwConvMode, dwSentMode ); + + dwConvMode |= cmode_add; + dwSentMode |= smode_add; + + ImmSetConversionStatus( hImc, dwConvMode, dwSentMode ); + + ImmReleaseContext( hwnd, hImc ); + } + } + + bool ConvMatches( DWORD convFlags ) + { + // To match, the active flags have to have none of the remove flags and have to have all of the "add" flags + if ( convFlags & cmode_remove ) + return false; + + if ( ( convFlags & cmode_add ) == cmode_add ) + { + return true; + } + return false; + } + + bool SentMatches( DWORD sentFlags ) + { + // To match, the active flags have to have none of the remove flags and have to have all of the "add" flags + if ( sentFlags & smode_remove ) + return false; + + if ( ( sentFlags & smode_add ) == smode_add ) + { + return true; + } + return false; + } + + unsigned int cmode_remove; + unsigned int cmode_add; + unsigned int smode_remove; + unsigned int smode_add; +}; + +static IMESettingsTransform g_ConversionMode_CHT_ToChinese( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + 0, + 0 ); +static IMESettingsTransform g_ConversionMode_CHT_ToEnglish( + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_CHS_ToChinese( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + 0, + 0 ); +static IMESettingsTransform g_ConversionMode_CHS_ToEnglish( + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_KO_ToKorean( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_KO_ToEnglish( + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_Hiragana( + IME_CMODE_ALPHANUMERIC | IME_CMODE_KATAKANA, + IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_DirectInput( + IME_CMODE_NATIVE | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ) | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_FullwidthKatakana( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN | IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_HalfwidthKatakana( + IME_CMODE_ALPHANUMERIC | IME_CMODE_FULLSHAPE, + IME_CMODE_NATIVE | IME_CMODE_ROMAN | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ), + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_FullwidthAlphanumeric( + IME_CMODE_NATIVE | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ), + IME_CMODE_ALPHANUMERIC | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_HalfwidthAlphanumeric( + IME_CMODE_NATIVE | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ) | IME_CMODE_FULLSHAPE, + IME_CMODE_ALPHANUMERIC | IME_CMODE_ROMAN, + 0, + 0 ); + +#endif // DO_IME + +int CInputSystem::GetIMEConversionModes( ConversionModeItem *dest, int destcount ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( dest ) + { + memset( dest, 0, destcount * sizeof( ConversionModeItem ) ); + } + + DWORD dwConvMode = 0, dwSentMode = 0; + + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode ); + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } + + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + switch ( info->languageflag ) + { + default: + return 0; + case TRADITIONAL_CHINESE: + // This is either native or alphanumeric + if ( dest ) + { + ConversionModeItem *item; + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Chinese", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHT_ToChinese; + item->active = g_ConversionMode_CHT_ToChinese.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHT_ToEnglish; + item->active = g_ConversionMode_CHT_ToEnglish.ConvMatches( dwConvMode ); + } + return 2; + case JAPANESE: + // There are 6 Japanese modes + if ( dest ) + { + ConversionModeItem *item; + + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Hiragana", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_Hiragana; + item->active = g_ConversionMode_JP_Hiragana.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_FullWidthKatakana", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_FullwidthKatakana; + item->active = g_ConversionMode_JP_FullwidthKatakana.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_FullWidthAlphanumeric", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_FullwidthAlphanumeric; + item->active = g_ConversionMode_JP_FullwidthAlphanumeric.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_HalfWidthKatakana", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_HalfwidthKatakana; + item->active = g_ConversionMode_JP_HalfwidthKatakana.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_HalfWidthAlphanumeric", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_HalfwidthAlphanumeric; + item->active = g_ConversionMode_JP_HalfwidthAlphanumeric.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_DirectInput; + item->active = g_ConversionMode_JP_DirectInput.ConvMatches( dwConvMode ); + + } + return 6; + case KOREAN: + // This is either native or alphanumeric + if ( dest ) + { + ConversionModeItem *item; + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Korean", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_KO_ToKorean; + item->active = g_ConversionMode_KO_ToKorean.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_KO_ToEnglish; + item->active = g_ConversionMode_KO_ToEnglish.ConvMatches( dwConvMode ); + } + return 2; + case SIMPLIFIED_CHINESE: + // This is either native or alphanumeric + if ( dest ) + { + ConversionModeItem *item; + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Chinese", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHS_ToChinese; + item->active = g_ConversionMode_CHS_ToChinese.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHS_ToChinese; + item->active = g_ConversionMode_CHS_ToChinese.ConvMatches( dwConvMode ); + } + return 2; + } +#endif + + return 0; +} + +#ifdef DO_IME + +static IMESettingsTransform g_SentenceMode_JP_None( + 0, + 0, + IME_SMODE_PLAURALCLAUSE | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_PHRASEPREDICT | IME_SMODE_CONVERSATION, + IME_SMODE_NONE ); + +static IMESettingsTransform g_SentenceMode_JP_General( + 0, + 0, + IME_SMODE_NONE | IME_SMODE_PLAURALCLAUSE | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_CONVERSATION, + IME_SMODE_PHRASEPREDICT + ); + +static IMESettingsTransform g_SentenceMode_JP_BiasNames( + 0, + 0, + IME_SMODE_NONE | IME_SMODE_PHRASEPREDICT | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_CONVERSATION, + IME_SMODE_PLAURALCLAUSE + ); + +static IMESettingsTransform g_SentenceMode_JP_BiasSpeech( + 0, + 0, + IME_SMODE_NONE | IME_SMODE_PHRASEPREDICT | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_PLAURALCLAUSE, + IME_SMODE_CONVERSATION + ); + +#endif // _X360 + +int CInputSystem::GetIMESentenceModes( SentenceModeItem *dest, int destcount ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( dest ) + { + memset( dest, 0, destcount * sizeof( SentenceModeItem ) ); + } + + DWORD dwConvMode = 0, dwSentMode = 0; + + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode ); + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } + + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + switch ( info->languageflag ) + { + default: + return 0; +// case TRADITIONAL_CHINESE: +// break; + case JAPANESE: + // There are 4 Japanese sentence modes + if ( dest ) + { + SentenceModeItem *item; + + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_General", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_General; + item->active = g_SentenceMode_JP_General.SentMatches( dwSentMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_BiasNames", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_BiasNames; + item->active = g_SentenceMode_JP_BiasNames.SentMatches( dwSentMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_BiasSpeech", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_BiasSpeech; + item->active = g_SentenceMode_JP_BiasSpeech.SentMatches( dwSentMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_NoConversion", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_None; + item->active = g_SentenceMode_JP_None.SentMatches( dwSentMode ); + } + return 4; + } +#endif + + return 0; +} + +void CInputSystem::OnChangeIMEConversionModeByHandle( int handleValue ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( handleValue == 0 ) + return; + + IMESettingsTransform *txform = ( IMESettingsTransform * )handleValue; + txform->Apply( (HWND)GetIMEWindow() ); +#endif +} + +void CInputSystem::OnChangeIMESentenceModeByHandle( int handleValue ) +{ +} + +void CInputSystem::OnInputLanguageChanged() +{ +} + +void CInputSystem::OnIMEStartComposition() +{ +} + +#ifdef DO_IME +void DescribeIMEFlag( char const *string, bool value ) +{ + if ( value ) + { + Msg( " %s\n", string ); + } +} + +#define IMEDesc( x ) DescribeIMEFlag( #x, flags & x ); +#endif // DO_IME + +void CInputSystem::OnIMEComposition( int flags ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + /* + Msg( "OnIMEComposition\n" ); + + IMEDesc( VGUI_GCS_COMPREADSTR ); + IMEDesc( VGUI_GCS_COMPREADATTR ); + IMEDesc( VGUI_GCS_COMPREADCLAUSE ); + IMEDesc( VGUI_GCS_COMPSTR ); + IMEDesc( VGUI_GCS_COMPATTR ); + IMEDesc( VGUI_GCS_COMPCLAUSE ); + IMEDesc( VGUI_GCS_CURSORPOS ); + IMEDesc( VGUI_GCS_DELTASTART ); + IMEDesc( VGUI_GCS_RESULTREADSTR ); + IMEDesc( VGUI_GCS_RESULTREADCLAUSE ); + IMEDesc( VGUI_GCS_RESULTSTR ); + IMEDesc( VGUI_GCS_RESULTCLAUSE ); + IMEDesc( VGUI_CS_INSERTCHAR ); + IMEDesc( VGUI_CS_NOMOVECARET ); + */ + + HIMC hIMC = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hIMC ) + { + if ( flags & VGUI_GCS_RESULTSTR ) + { + wchar_t tempstr[ 32 ]; + + int len = ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, (LPVOID)tempstr, sizeof( tempstr ) ); + if ( len > 0 ) + { + if ((len % 2) != 0) + len++; + int numchars = len / sizeof( wchar_t ); + + for ( int i = 0; i < numchars; ++i ) + { + InternalKeyTyped( tempstr[ i ] ); + } + } + } + if ( flags & VGUI_GCS_COMPSTR ) + { + wchar_t tempstr[ 256 ]; + + int len = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, (LPVOID)tempstr, sizeof( tempstr ) ); + if ( len > 0 ) + { + if ((len % 2) != 0) + len++; + int numchars = len / sizeof( wchar_t ); + tempstr[ numchars ] = L'\0'; + + InternalSetCompositionString( tempstr ); + } + } + + ImmReleaseContext( ( HWND )GetIMEWindow(), hIMC ); + } +#endif +} + +void CInputSystem::OnIMEEndComposition() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + // tell the current focused panel that a key was typed + PostKeyMessage( new KeyValues( "DoCompositionString", "string", L"" ) ); + } +} + +void CInputSystem::DestroyCandidateList() +{ +#ifdef DO_IME + if ( _imeCandidates ) + { + delete[] (char *)_imeCandidates; + _imeCandidates = null; + } +#endif +} + +void CInputSystem::OnIMEShowCandidates() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + DestroyCandidateList(); + CreateNewCandidateList(); + + InternalShowCandidateWindow(); +#endif +} + +void CInputSystem::OnIMECloseCandidates() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + InternalHideCandidateWindow(); + DestroyCandidateList(); +#endif +} + +void CInputSystem::OnIMEChangeCandidates() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + DestroyCandidateList(); + CreateNewCandidateList(); + + InternalUpdateCandidateWindow(); +#endif +} + +void CInputSystem::CreateNewCandidateList() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + Assert( !_imeCandidates ); + + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + DWORD numCandidates = 0; + + DWORD bytes = ImmGetCandidateListCountW( hImc, &numCandidates ); + if ( numCandidates > 0 ) + { + DWORD buflen = bytes + 1; + + char *buf = new char[ buflen ]; + Q_memset( buf, 0, buflen ); + + CANDIDATELIST *list = ( CANDIDATELIST *)buf; + DWORD copyBytes = ImmGetCandidateListW( hImc, 0, list, buflen ); + if ( copyBytes > 0 ) + { + _imeCandidates = list; + } + else + { + delete[] buf; + } + } + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } +#endif +} + +int CInputSystem::GetCandidateListCount() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + + return (int)_imeCandidates->dwCount; +#else + return 0; +#endif +} + +void CInputSystem::GetCandidate( int num, wchar_t *dest, int destSizeBytes ) +{ + ASSERT_IF_IME_NYI(); + + dest[ 0 ] = L'\0'; +#ifdef DO_IME + if ( num < 0 || num >= (int)_imeCandidates->dwCount ) + { + return; + } + + DWORD offset = *( DWORD *)( (char *)( _imeCandidates->dwOffset + num ) ); + wchar_t *s = ( wchar_t *)( (char *)_imeCandidates + offset ); + + wcsncpy( dest, s, destSizeBytes / sizeof( wchar_t ) - 1 ); + dest[ destSizeBytes / sizeof( wchar_t ) - 1 ] = L'\0'; +#endif +} + +int CInputSystem::GetCandidateListSelectedItem() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + + return (int)_imeCandidates->dwSelection; +#else + return 0; +#endif +} + +int CInputSystem::GetCandidateListPageSize() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + return (int)_imeCandidates->dwPageSize; +#else + return 0; +#endif +} + +int CInputSystem::GetCandidateListPageStart() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + return (int)_imeCandidates->dwPageStart; +#else + return 0; +#endif +} + +void CInputSystem::SetCandidateListPageStart( int start ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + ImmNotifyIME( hImc, NI_SETCANDIDATE_PAGESTART, 0, start ); + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } +#endif +} + +void CInputSystem::OnIMERecomputeModes() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CInputSystem::CandidateListStartsAtOne() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + DWORD prop = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY ); + if ( prop & IME_PROP_CANDLIST_START_FROM_1 ) + { + return true; + } +#endif + return false; +} + +void CInputSystem::SetCandidateWindowPos( int x, int y ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + POINT point; + CANDIDATEFORM Candidate; + + point.x = x; + point.y = y; + + HIMC hIMC = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hIMC ) + { + // Set candidate window position near caret position + Candidate.dwIndex = 0; + Candidate.dwStyle = CFS_FORCE_POSITION; + Candidate.ptCurrentPos.x = point.x; + Candidate.ptCurrentPos.y = point.y; + ImmSetCandidateWindow( hIMC, &Candidate ); + + ImmReleaseContext( ( HWND )GetIMEWindow(),hIMC ); + } +#endif +} + +void CInputSystem::InternalSetCompositionString( const wchar_t *compstr ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + // tell the current focused panel that a key was typed + PostKeyMessage( new KeyValues( "DoCompositionString", "string", compstr ) ); + } +} + +void CInputSystem::InternalShowCandidateWindow() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + PostKeyMessage( new KeyValues( "DoShowIMECandidates" ) ); + } +} + +void CInputSystem::InternalHideCandidateWindow() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + PostKeyMessage( new KeyValues( "DoHideIMECandidates" ) ); + } +} + +void CInputSystem::InternalUpdateCandidateWindow() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + PostKeyMessage( new KeyValues( "DoUpdateIMECandidates" ) ); + } +} + +bool CInputSystem::GetShouldInvertCompositionString() +{ +#ifdef DO_IME + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + if ( !info ) + return false; + + // Only Chinese (simplified and traditional) + return info->invertcomposition; +#else + return false; +#endif +} + +void CInputSystem::RegisterKeyCodeUnhandledListener( VPANEL panel ) +{ + if ( !panel ) + return; + + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + VPanel *listener = (VPanel *)panel; + + if ( pContext->m_KeyCodeUnhandledListeners.Find( listener ) == pContext->m_KeyCodeUnhandledListeners.InvalidIndex() ) + { + pContext->m_KeyCodeUnhandledListeners.AddToTail( listener ); + } +} + +void CInputSystem::UnregisterKeyCodeUnhandledListener( VPANEL panel ) +{ + if ( !panel ) + return; + + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + VPanel *listener = (VPanel *)panel; + + pContext->m_KeyCodeUnhandledListeners.FindAndRemove( listener ); +} + + +// Posts unhandled message to all interested panels +void CInputSystem::OnKeyCodeUnhandled( int keyCode ) +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + int c = pContext->m_KeyCodeUnhandledListeners.Count(); + for ( int i = 0; i < c; ++i ) + { + VPanel *listener = pContext->m_KeyCodeUnhandledListeners[ i ]; + g_pIVgui->PostMessage((VPANEL)listener, new KeyValues( "KeyCodeUnhandled", "code", keyCode ), NULL ); + } +} + +void CInputSystem::PostModalSubTreeMessage( VPanel *subTree, bool state ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if( pContext->m_pModalSubTree == NULL ) + return; + + //tell the current focused panel that a key was released + KeyValues *kv = new KeyValues( "ModalSubTree", "state", state ? 1 : 0 ); + g_pIVgui->PostMessage( (VPANEL)pContext->m_pModalSubTree, kv, NULL ); +} + +// Assumes subTree is a child panel of the root panel for the vgui contect +// if restrictMessagesToSubTree is true, then mouse and kb messages are only routed to the subTree and it's children and mouse/kb focus +// can only be on one of the subTree children, if a mouse click occurs outside of the subtree, and "UnhandledMouseClick" message is sent to unhandledMouseClickListener panel +// if it's set +// if restrictMessagesToSubTree is false, then mouse and kb messages are routed as normal except that they are not routed down into the subtree +// however, if a mouse click occurs outside of the subtree, and "UnhandleMouseClick" message is sent to unhandledMouseClickListener panel +// if it's set +void CInputSystem::SetModalSubTree( VPANEL subTree, VPANEL unhandledMouseClickListener, bool restrictMessagesToSubTree /*= true*/ ) +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + if ( pContext->m_pModalSubTree && + pContext->m_pModalSubTree != (VPanel *)subTree ) + { + ReleaseModalSubTree(); + } + + if ( !subTree ) + return; + + pContext->m_pModalSubTree = (VPanel *)subTree; + pContext->m_pUnhandledMouseClickListener = (VPanel *)unhandledMouseClickListener; + pContext->m_bRestrictMessagesToModalSubTree = restrictMessagesToSubTree; + + PostModalSubTreeMessage( pContext->m_pModalSubTree, true ); +} + +void CInputSystem::ReleaseModalSubTree() +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + if ( pContext->m_pModalSubTree ) + { + PostModalSubTreeMessage( pContext->m_pModalSubTree, false ); + } + + pContext->m_pModalSubTree = NULL; + pContext->m_pUnhandledMouseClickListener = NULL; + pContext->m_bRestrictMessagesToModalSubTree = false; + +} + +VPANEL CInputSystem::GetModalSubTree() +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return 0; + + return (VPANEL)pContext->m_pModalSubTree; +} + +// These toggle whether the modal subtree is exclusively receiving messages or conversely whether it's being excluded from receiving messages +void CInputSystem::SetModalSubTreeReceiveMessages( bool state ) +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + Assert( pContext->m_pModalSubTree ); + if ( !pContext->m_pModalSubTree ) + return; + + pContext->m_bRestrictMessagesToModalSubTree = state; + +} + +bool CInputSystem::ShouldModalSubTreeReceiveMessages() const +{ + InputContext_t *pContext = const_cast< CInputSystem * >( this )->GetInputContext(m_hContext); + if ( !pContext ) + return true; + + return pContext->m_bRestrictMessagesToModalSubTree; +} diff --git a/vgui2/src/LocalizedStringTable.cpp b/vgui2/src/LocalizedStringTable.cpp new file mode 100644 index 0000000..4e34164 --- /dev/null +++ b/vgui2/src/LocalizedStringTable.cpp @@ -0,0 +1,1060 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + + +#pragma warning( disable: 4018 ) // '==' : signed/unsigned mismatch in rbtree +#if defined( WIN32 ) && !defined( _X360 ) +#include <windows.h> +#elif defined( POSIX ) +#include <iconv.h> + +#endif +#include <wchar.h> + +#include "filesystem.h" + +#include "vgui_internal.h" +#include "vgui/ILocalize.h" +#include "vgui/ISystem.h" +#include "vgui/ISurface.h" + +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier1/utlstring.h" +#include "UnicodeFileHelpers.h" +#include "tier0/icommandline.h" +#include "byteswap.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +#define MAX_LOCALIZED_CHARS 4096 + +//----------------------------------------------------------------------------- +// +// Internal implementation +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: Maps token names to localized unicode strings +//----------------------------------------------------------------------------- +class CLocalizedStringTable : public vgui::ILocalize +{ +public: + CLocalizedStringTable(); + ~CLocalizedStringTable(); + + // adds the contents of a file to the localization table + virtual bool AddFile( const char *fileName, const char *pPathID, bool bIncludeFallbackSearchPaths ); + + // saves the entire contents of the token tree to the file + bool SaveToFile( const char *fileName ); + + // adds a single name/unicode string pair to the table + void AddString(const char *tokenName, wchar_t *unicodeString, const char *fileName); + + // Finds the localized text for pName + wchar_t *Find(const char *pName); + + // finds the index of a token by token name + StringIndex_t FindIndex(const char *pName); + + // Remove all strings in the table. + void RemoveAll(); + + // iteration functions + StringIndex_t GetFirstStringIndex(); + + // returns the next index, or INVALID_LOCALIZE_STRING_INDEX if no more strings available + StringIndex_t GetNextStringIndex(StringIndex_t index); + + // gets the values from the index + const char *GetNameByIndex(StringIndex_t index); + wchar_t *GetValueByIndex(StringIndex_t index); + const char *GetFileNameByIndex(StringIndex_t index); + + // sets the value in the index + // has bad memory characteristics, should only be used in the editor + void SetValueByIndex(StringIndex_t index, wchar_t *newValue); + + // iterates the filenames + int GetLocalizationFileCount(); + const char *GetLocalizationFileName(int index); + + // returns whether a file has already been loaded + bool LocalizationFileIsLoaded( const char *name ); + + const char *FindAsUTF8( const char *pchTokenName ); + + virtual void ConstructString(wchar_t *unicodeOutput, int unicodeBufferSizeInBytes, const char *tokenName, KeyValues *localizationVariables); + virtual void ConstructString(wchar_t *unicodeOutput, int unicodeBufferSizeInBytes, StringIndex_t unlocalizedTextSymbol, KeyValues *localizationVariables); + +private: + // for development only, reloads localization files + virtual void ReloadLocalizationFiles( ); + + bool AddAllLanguageFiles( const char *baseFileName ); + + void BuildFastValueLookup(); + void DiscardFastValueLookup(); + int FindExistingValueIndex( const wchar_t *value ); + + char m_szLanguage[64]; + bool m_bUseOnlyLongestLanguageString; + + struct localizedstring_t + { + StringIndex_t nameIndex; + // nameIndex == INVALID_LOCALIZE_STRING_INDEX is used only for searches and implies + // that pszValueString will be used from union fields. + union + { + StringIndex_t valueIndex; // Used when nameIndex != INVALID_LOCALIZE_STRING_INDEX + char const * pszValueString; // Used only if nameIndex == INVALID_LOCALIZE_STRING_INDEX + }; + CUtlSymbol filename; + }; + + // Stores the symbol lookup + CUtlRBTree<localizedstring_t, StringIndex_t> m_Lookup; + + // stores the string data + CUtlVector<char> m_Names; + CUtlVector<wchar_t> m_Values; + CUtlSymbol m_CurrentFile; + + struct LocalizationFileInfo_t + { + CUtlSymbol symName; + CUtlSymbol symPathID; + bool bIncludeFallbacks; + + static bool LessFunc( const LocalizationFileInfo_t& lhs, const LocalizationFileInfo_t& rhs ) + { + int iresult = Q_stricmp( lhs.symPathID.String(), rhs.symPathID.String() ); + if ( iresult != 0 ) + { + return iresult == -1; + } + + return Q_stricmp( lhs.symName.String(), rhs.symName.String() ) < 0; + } + }; + + CUtlVector< LocalizationFileInfo_t > m_LocalizationFiles; + + struct fastvalue_t + { + int valueindex; + const wchar_t *search; + + static CLocalizedStringTable *s_pTable; + }; + + CUtlRBTree< fastvalue_t, int > m_FastValueLookup; + + static CLocalizedStringTable *s_pTable; + + // Less function, for sorting strings + static bool SymLess( localizedstring_t const& i1, localizedstring_t const& i2 ); + + static bool FastValueLessFunc( const fastvalue_t& lhs, const fastvalue_t& rhs ); +}; + +// global instance of table +CLocalizedStringTable g_StringTable; + +// expose the interface +EXPOSE_SINGLE_INTERFACE_GLOBALVAR_WITH_NAMESPACE(CLocalizedStringTable, vgui::, ILocalize, VGUI_LOCALIZE_INTERFACE_VERSION, g_StringTable); + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CLocalizedStringTable::CLocalizedStringTable() : + m_Lookup( 0, 0, SymLess ), m_Names( 1024 ), m_Values( 2048 ), m_FastValueLookup( 0, 0, FastValueLessFunc ) +{ + m_bUseOnlyLongestLanguageString = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CLocalizedStringTable::~CLocalizedStringTable() +{ + m_Names.Purge(); + m_Values.Purge(); + m_LocalizationFiles.Purge(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds the contents of a file +//----------------------------------------------------------------------------- +bool CLocalizedStringTable::AddFile( const char *szFileName, const char *pPathID, bool bIncludeFallbackSearchPaths ) +{ + // use the correct file based on the chosen language + static const char *const LANGUAGE_STRING = "%language%"; + static const char *const ENGLISH_STRING = "english"; + static const int MAX_LANGUAGE_NAME_LENGTH = 64; + char language[MAX_LANGUAGE_NAME_LENGTH]; + char fileName[MAX_PATH]; + int offs = 0; + bool success = false; + + memset( language, 0, sizeof(language) ); + + Q_strncpy( fileName, szFileName, sizeof( fileName ) ); + + // Lowercase the *relative* portion of the filename, + // in case people look for "Resource/file.txt" etc. We always + // use lowercase filenames for files in the game filesystem. + V_strlower( fileName ); + + const char *langptr = strstr(szFileName, LANGUAGE_STRING); + if (langptr) + { + // LOAD THE ENGLISH FILE FIRST + // always load the file to make sure we're not missing any strings + // copy out the initial part of the string + offs = langptr - szFileName; + strncpy(fileName, szFileName, offs); + fileName[offs] = 0; + + if ( vgui::g_pSystem->CommandLineParamExists("-all_languages") ) + { + m_bUseOnlyLongestLanguageString = true; + return AddAllLanguageFiles( fileName ); + } + + // append "english" as our default language + Q_strncat(fileName, ENGLISH_STRING, sizeof( fileName ), COPY_ALL_CHARACTERS ); + + // append the end of the initial string + offs += strlen(LANGUAGE_STRING); + Q_strncat(fileName, szFileName + offs, sizeof( fileName ), COPY_ALL_CHARACTERS); + + success = AddFile( fileName, pPathID, bIncludeFallbackSearchPaths ); + + bool bValid; + if ( IsPC() ) + { + bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof(language)-1 ); + } + else + { + Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) ); + bValid = true; + } + + // LOAD THE LOCALIZED FILE IF IT'S NOT ENGLISH + // append the language + if ( bValid ) + { + if ( strlen(language) != 0 && stricmp(language, ENGLISH_STRING) != 0 ) + { + // copy out the initial part of the string + offs = langptr - szFileName; + strncpy(fileName, szFileName, offs); + fileName[offs] = 0; + + Q_strncat(fileName, language, sizeof( fileName ), COPY_ALL_CHARACTERS); + + // append the end of the initial string + offs += strlen(LANGUAGE_STRING); + Q_strncat(fileName, szFileName + offs, sizeof( fileName ), COPY_ALL_CHARACTERS ); + + success &= AddFile( fileName, pPathID, bIncludeFallbackSearchPaths ); + } + } + return success; + } + + // store the localization file name if it doesn't already exist + LocalizationFileInfo_t search; + search.symName = fileName; + search.symPathID = pPathID ? pPathID : ""; + search.bIncludeFallbacks = bIncludeFallbackSearchPaths; + + int lfc = m_LocalizationFiles.Count(); + for ( int lf = 0; lf < lfc; ++lf ) + { + LocalizationFileInfo_t& entry = m_LocalizationFiles[ lf ]; + if ( !Q_stricmp( entry.symName.String(), fileName ) ) + { + m_LocalizationFiles.Remove( lf ); + break; + } + } + + m_LocalizationFiles.AddToTail( search ); + + // This will give us a list of paths from highest to lowest precedence: e.g.: + // for "GAME" when running -game episodic, it'll show: + // "basedir/episodic/;basedir/hl2" + // We do this manually instead of just asking for the first match to support bIncludeFallbackSearchPaths + char searchPaths[ MAX_PATH*50 ] = { 0 }; // allow for 50 search paths + + Verify( g_pFullFileSystem->GetSearchPath( pPathID, true, searchPaths, sizeof( searchPaths ) ) < sizeof(searchPaths) ); + + CUtlSymbolTable pathStrings; + CUtlVector< CUtlSymbol > searchList; + + bool bIsFullPath = false; + if ( V_IsAbsolutePath( fileName ) ) + { + bIsFullPath = true; + CUtlSymbol sym = pathStrings.AddString( fileName ); + searchList.AddToHead( sym ); + } + else + { + // We want to walk them in reverse order so newer files are "overrides" for older ones, so we add them to a list in reverse order + for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) ) + { + if ( IsX360() && ( g_pFullFileSystem->GetDVDMode() == DVDMODE_STRICT ) && !V_stristr( path, ".zip" ) ) + { + // only want zip paths + continue; + } + + char fullpath[MAX_PATH]; + V_strcpy_safe( fullpath, path ); + V_AppendSlash( fullpath, sizeof(fullpath) ); + V_strcat_safe( fullpath, fileName ); + Q_FixSlashes( fullpath ); + //Q_strlower( fullpath ); // NO! This screws up Linux + + CUtlSymbol sym = pathStrings.AddString( fullpath ); + // With bIncludeFallbackSearchPaths we iterate overriding as we go, so push them in reverse order so the + // highest precendence search paths have the highest precendence. Otherwise push them in order, as we'll + // only process the first one. + if ( !bIncludeFallbackSearchPaths ) + { + searchList.AddToTail( sym ); + } + else + { + searchList.AddToHead( sym ); + } + } + } + + bool first = true; + bool bLoadedAtLeastOne = false; + + for ( int sp = 0; sp < searchList.Count(); ++sp ) + { + const char *fullpath = pathStrings.String( searchList[ sp ] ); + + // parse out the file + FileHandle_t file = g_pFullFileSystem->Open( fullpath, "rb" ); + if (!file) + { + continue; + } + + if ( first ) + { + first = false; + } + else if ( !bIncludeFallbackSearchPaths ) + { + g_pFullFileSystem->Close(file); + break; + } + + bLoadedAtLeastOne = true; + + // this is an optimization so that the filename string doesn't have to get converted to a symbol for each key/value + m_CurrentFile = fullpath; + + // read into a memory block + int fileSize = g_pFullFileSystem->Size(file); + int bufferSize = g_pFullFileSystem->GetOptimalReadSize( file, fileSize + sizeof(ucs2) ); + ucs2 *memBlock = (ucs2 *)g_pFullFileSystem->AllocOptimalReadBuffer(file, bufferSize); + bool bReadOK = ( g_pFullFileSystem->ReadEx(memBlock, bufferSize, fileSize, file) != 0 ); + + // finished with file + g_pFullFileSystem->Close(file); + + // null-terminate the stream + memBlock[fileSize / sizeof(ucs2)] = 0x0000; + + // check the first character, make sure this a little-endian unicode file + ucs2 *data = memBlock; + ucs2 signature = LittleShort( data[0] ); + if ( !bReadOK || signature != 0xFEFF ) + { + Msg( "Ignoring non-unicode close caption file %s\n", fullpath ); + g_pFullFileSystem->FreeOptimalReadBuffer( memBlock ); + return false; + } + + // ensure little-endian unicode reads correctly on all platforms + CByteswap byteSwap; + byteSwap.SetTargetBigEndian( false ); + byteSwap.SwapBufferToTargetEndian( data, data, fileSize / sizeof(ucs2) ); + + // skip past signature + data++; + + // parse out a token at a time + enum states_e + { + STATE_BASE, // looking for base settings + STATE_TOKENS, // reading in unicode tokens + }; + + bool bQuoted; + bool bEnglishFile = false; + if ( strstr(fullpath, "_english.txt") ) + { + bEnglishFile = true; + } + + bool spew = false; + if ( CommandLine()->FindParm( "-ccsyntax" ) ) + { + spew = true; + } + + BuildFastValueLookup(); + + states_e state = STATE_BASE; + while (1) + { + // read the key and the value + ucs2 keytoken[128]; + ucs2 *pchNewdata = ReadUnicodeToken(data, keytoken, ARRAYSIZE(keytoken), bQuoted); + if (!keytoken[0]) + break; // we've hit the null terminator + + // convert the token to a string + char key[128]; + V_UCS2ToUTF8(keytoken, key, static_cast<int>( sizeof(key) )); + data = pchNewdata; + + // if we have a C++ style comment, read to end of line and continue + if (!strnicmp(key, "//", 2)) + { + data = ReadToEndOfLine(data); + continue; + } + + if ( spew ) + { + Msg( "%s\n", key ); + } + + ucs2 valuetoken[ MAX_LOCALIZED_CHARS ]; + data = ReadUnicodeToken(data, valuetoken, MAX_LOCALIZED_CHARS, bQuoted); + if (!valuetoken[0] && !bQuoted) + break; // we've hit the null terminator + + if (state == STATE_BASE) + { + if (!stricmp(key, "Language")) + { + // copy out our language setting + char value[MAX_LOCALIZED_CHARS]; + V_UCS2ToUTF8(valuetoken, value, sizeof(value)); + strncpy(m_szLanguage, value, sizeof(m_szLanguage) - 1); + } + else if (!stricmp(key, "Tokens")) + { + state = STATE_TOKENS; + } + else if (!stricmp(key, "}")) + { + // we've hit the end + break; + } + } + else if (state == STATE_TOKENS) + { + if (!stricmp(key, "}")) + { + // end of tokens + state = STATE_BASE; + } + else + { + // skip our [english] beginnings (in non-english files) + if ( (bEnglishFile) || (!bEnglishFile && strnicmp(key, "[english]", 9))) + { + // Check for a conditional tag + bool bAccepted = true; + ucs2 conditional[ MAX_LOCALIZED_CHARS ]; + ucs2 *tempData = ReadUnicodeToken(data, conditional, MAX_LOCALIZED_CHARS, bQuoted); + if ( !bQuoted && conditional[0] == L'[' && conditional[1] == L'$' ) // wcsstr( conditional, L"[$" ) ) + { + // Evaluate the conditional tag + char cond[MAX_LOCALIZED_CHARS]; + V_UCS2ToUTF8(conditional, cond, sizeof(cond)); + bAccepted = EvaluateConditional( cond ); + + // Robin: HACK: Cheesy support for language-based filtering. Main has much better + // support for this, in all KV files, so this will be obsoleted in post-TF2 products. + char *pszKey = &cond[2]; + bool bNot = false; + if ( pszKey[0] == '!' ) + { + bNot = true; + pszKey++; + } + // Trim off the ] + if ( pszKey && pszKey[0] ) + { + pszKey[ V_strlen(pszKey)-1 ] = '\0'; + if ( !V_stricmp( pszKey, "ENGLISH" ) || + !V_stricmp( pszKey, "JAPANESE" ) || + !V_stricmp( pszKey, "GERMAN" ) || + !V_stricmp( pszKey, "FRENCH" ) || + !V_stricmp( pszKey, "SPANISH" ) || + !V_stricmp( pszKey, "ITALIAN" ) || + !V_stricmp( pszKey, "KOREAN" ) || + !V_stricmp( pszKey, "TCHINESE" ) || + !V_stricmp( pszKey, "PORTUGUESE" ) || + !V_stricmp( pszKey, "SCHINESE" ) || + !V_stricmp( pszKey, "POLISH" ) || + !V_stricmp( pszKey, "RUSSIAN" ) ) + { + // the language symbols are true if we are in that language + // english is assumed when no language is present + const char *pLanguageString; +#ifdef _X360 + pLanguageString = XBX_GetLanguageString(); +#else + static ConVarRef cl_language( "cl_language" ); + pLanguageString = cl_language.GetString(); +#endif + if ( !pLanguageString || !pLanguageString[0] ) + { + pLanguageString = "english"; + } + bool bMatched = ( !V_stricmp( pszKey, pLanguageString ) ); + bAccepted = (bMatched && !bNot) || (!bMatched && bNot); + } + } + + data = tempData; + } + if ( bAccepted ) + { + wchar_t fullString[MAX_LOCALIZED_CHARS+1]; + int i = 0; + for ( i = 0; i < MAX_LOCALIZED_CHARS && valuetoken[i] != 0; i++ ) + fullString[i] = valuetoken[i]; // explode the ucs2 into a wchar_t wide buffer + fullString[i] = 0; + + // add the string to the table + AddString(key, fullString, NULL); + } + } + } + } + } + + g_pFullFileSystem->FreeOptimalReadBuffer( memBlock ); + } + + if ( !bLoadedAtLeastOne ) + { + Warning("CLocalizedStringTable::AddFile() failed to load file \"%s\".\n", szFileName ); + } + + DiscardFastValueLookup(); + m_CurrentFile = UTL_INVAL_SYMBOL; + return bLoadedAtLeastOne; +} + +//----------------------------------------------------------------------------- +// Purpose: Load all the localized language strings, and uses the longest string from each language +//----------------------------------------------------------------------------- +bool CLocalizedStringTable::AddAllLanguageFiles( const char *baseFileName ) +{ + bool success = true; + + // work out the path the files are in + char szFilePath[MAX_PATH]; + Q_strncpy( szFilePath, baseFileName, sizeof(szFilePath) ); + char *lastSlash = strrchr( szFilePath, '\\' ); + if (!lastSlash) + { + lastSlash = strrchr( szFilePath, '/' ); + } + if (lastSlash) + { + lastSlash[1] = 0; + } + else + { + szFilePath[0] = 0; + } + + // iterate through and add all the languages (for development) + // the longest string out of all the languages will be used + char szSearchPath[MAX_PATH]; + Q_snprintf( szSearchPath, sizeof(szSearchPath), "%s*.txt", baseFileName ); + + FileFindHandle_t hFind = NULL; + const char *file = g_pFullFileSystem->FindFirst( szSearchPath, &hFind ); + while ( file ) + { + // re-add in the search path + char szFile[MAX_PATH]; + Q_snprintf( szFile, sizeof(szFile), "%s%s", szFilePath, file ); + + // add the file + success &= AddFile( szFile, NULL, true ); + + // next file + file = g_pFullFileSystem->FindNext( hFind ); + } + g_pFullFileSystem->FindClose( hFind ); + return success; +} + +//----------------------------------------------------------------------------- +// Purpose: saves the entire contents of the token tree to the file +//----------------------------------------------------------------------------- +bool CLocalizedStringTable::SaveToFile( const char *szFileName ) +{ + // parse out the file + FileHandle_t file = g_pFullFileSystem->Open(szFileName, "wb"); + if (!file) + return false; + + // only save the symbols relevant to this file + CUtlSymbol fileName = szFileName; + + // write litte-endian unicode marker + unsigned short marker = 0xFEFF; + marker = LittleShort( marker ); + g_pFullFileSystem->Write(&marker, sizeof( marker ), file); + + const char *startStr = "\"lang\"\r\n{\r\n\"Language\" \"English\"\r\n\"Tokens\"\r\n{\r\n"; + const char *endStr = "}\r\n}\r\n"; + + // write out the first string + static wchar_t unicodeString[1024]; + int strLength = ConvertANSIToUnicode(startStr, unicodeString, sizeof(unicodeString)); + if (!strLength) + return false; + + g_pFullFileSystem->Write(unicodeString, wcslen( unicodeString ) * sizeof(wchar_t), file); + + // convert our spacing characters to unicode +// wchar_t unicodeSpace = L' '; + wchar_t unicodeQuote = L'\"'; + wchar_t unicodeCR = L'\r'; + wchar_t unicodeNewline = L'\n'; + wchar_t unicodeTab = L'\t'; + + // write out all the key/value pairs + for (StringIndex_t idx = GetFirstStringIndex(); idx != INVALID_LOCALIZE_STRING_INDEX; idx = GetNextStringIndex(idx)) + { + // only write strings that belong in this file + if (fileName != m_Lookup[idx].filename) + continue; + + const char *name = GetNameByIndex(idx); + wchar_t *value = GetValueByIndex(idx); + + // convert the name to a unicode string + ConvertANSIToUnicode(name, unicodeString, sizeof(unicodeString)); + + g_pFullFileSystem->Write(&unicodeTab, sizeof(wchar_t), file); + + // write out + g_pFullFileSystem->Write(&unicodeQuote, sizeof(wchar_t), file); + g_pFullFileSystem->Write(unicodeString, wcslen( unicodeString ) * sizeof(wchar_t), file); + g_pFullFileSystem->Write(&unicodeQuote, sizeof(wchar_t), file); + + g_pFullFileSystem->Write(&unicodeTab, sizeof(wchar_t), file); + g_pFullFileSystem->Write(&unicodeTab, sizeof(wchar_t), file); + + g_pFullFileSystem->Write(&unicodeQuote, sizeof(wchar_t), file); + g_pFullFileSystem->Write(value, wcslen(value) * sizeof(wchar_t), file); + g_pFullFileSystem->Write(&unicodeQuote, sizeof(wchar_t), file); + + g_pFullFileSystem->Write(&unicodeCR, sizeof(wchar_t), file); + g_pFullFileSystem->Write(&unicodeNewline, sizeof(wchar_t), file); + } + + // write end string + strLength = ConvertANSIToUnicode(endStr, unicodeString, sizeof(unicodeString)); + g_pFullFileSystem->Write(unicodeString, strLength * sizeof(wchar_t), file); + + g_pFullFileSystem->Close(file); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: for development, reloads localization files +//----------------------------------------------------------------------------- +void CLocalizedStringTable::ReloadLocalizationFiles( ) +{ + // re-add all the localization files + for (int i = 0; i < m_LocalizationFiles.Count(); i++) + { + LocalizationFileInfo_t& entry = m_LocalizationFiles[ i ]; + AddFile + ( + entry.symName.String(), + entry.symPathID.String()[0] ? entry.symPathID.String() : NULL, + entry.bIncludeFallbacks + ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Used to sort strings +//----------------------------------------------------------------------------- +bool CLocalizedStringTable::SymLess(localizedstring_t const &i1, localizedstring_t const &i2) +{ + const char *str1 = (i1.nameIndex == INVALID_LOCALIZE_STRING_INDEX) ? i1.pszValueString : + &g_StringTable.m_Names[i1.nameIndex]; + const char *str2 = (i2.nameIndex == INVALID_LOCALIZE_STRING_INDEX) ? i2.pszValueString : + &g_StringTable.m_Names[i2.nameIndex]; + + return stricmp(str1, str2) < 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Finds a string in the table +//----------------------------------------------------------------------------- +wchar_t *CLocalizedStringTable::Find(const char *pName) +{ + StringIndex_t idx = FindIndex(pName); + if (idx == INVALID_LOCALIZE_STRING_INDEX) + return NULL; + + return &m_Values[m_Lookup[idx].valueIndex]; +} + +//----------------------------------------------------------------------------- +// Purpose: Finds a string in the table +//----------------------------------------------------------------------------- +const char *CLocalizedStringTable::FindAsUTF8( const char *pchTokenName ) +{ + wchar_t *pwch = Find( pchTokenName ); + if ( !pwch ) + return pchTokenName; + + static char rgchT[2048]; + Q_UnicodeToUTF8( pwch, rgchT, sizeof( rgchT ) ); + return rgchT; +} + + +//----------------------------------------------------------------------------- +// Purpose: finds the index of a token by token name +//----------------------------------------------------------------------------- +StringIndex_t CLocalizedStringTable::FindIndex(const char *pName) +{ + if (!pName) + return NULL; + + // strip the pound character (which is used elsewhere to indicate that it's a string that should be translated) + if (pName[0] == '#') + { + pName++; + } + + // Passing this special invalid symbol makes the comparison function + // use the string passed in the context + localizedstring_t invalidItem; + invalidItem.nameIndex = INVALID_LOCALIZE_STRING_INDEX; + invalidItem.pszValueString = pName; + return m_Lookup.Find( invalidItem ); +} + +//----------------------------------------------------------------------------- +// Finds and/or creates a symbol based on the string +//----------------------------------------------------------------------------- +void CLocalizedStringTable::AddString(const char *pString, wchar_t *pValue, const char *fileName) +{ + if (!pString) + return; + + MEM_ALLOC_CREDIT(); + + // see if the value is already in our string table + int valueIndex = FindExistingValueIndex( pValue ); + if ( valueIndex == INVALID_LOCALIZE_STRING_INDEX ) + { + int len = wcslen( pValue ) + 1; + valueIndex = m_Values.AddMultipleToTail( len ); + memcpy( &m_Values[valueIndex], pValue, len * sizeof(wchar_t) ); + } + + // see if the key is already in the table + StringIndex_t stridx = FindIndex( pString ); + localizedstring_t item; + item.nameIndex = stridx; + + if ( stridx == INVALID_LOCALIZE_STRING_INDEX ) + { + // didn't find, insert the string into the vector. + int len = strlen(pString) + 1; + stridx = m_Names.AddMultipleToTail( len ); + memcpy( &m_Names[stridx], pString, len * sizeof(char) ); + + item.nameIndex = stridx; + item.valueIndex = valueIndex; + item.filename = fileName ? fileName : m_CurrentFile; + + m_Lookup.Insert( item ); + } + else + { + // it's already in the table + + if ( m_bUseOnlyLongestLanguageString ) + { + // check which string is longer + wchar_t *newValue = pValue; + wchar_t *oldValue = GetValueByIndex( stridx ); + + // get the width of the string, using just the first font + int newWide, oldWide, tall; + vgui::g_pSurface->GetTextSize( 1, newValue, newWide, tall ); + vgui::g_pSurface->GetTextSize( 1, oldValue, oldWide, tall ); + + // if the new one is shorter, don't let it be added + if (newWide < oldWide) + return; + } + + // replace the current item + item.nameIndex = GetNameByIndex( stridx ) - &m_Names[ 0 ]; + item.valueIndex = valueIndex; + item.filename = fileName ? fileName : m_CurrentFile; + m_Lookup[ stridx ] = item; + } +} + +//----------------------------------------------------------------------------- +// Remove all symbols in the table. +//----------------------------------------------------------------------------- +void CLocalizedStringTable::RemoveAll() +{ + m_Lookup.RemoveAll(); + m_Names.RemoveAll(); + m_Values.RemoveAll(); + m_LocalizationFiles.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: iteration functions +//----------------------------------------------------------------------------- +StringIndex_t CLocalizedStringTable::GetFirstStringIndex() +{ + return m_Lookup.FirstInorder(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the next index, or INVALID_LOCALIZE_STRING_INDEX if no more strings available +//----------------------------------------------------------------------------- +StringIndex_t CLocalizedStringTable::GetNextStringIndex(StringIndex_t index) +{ + StringIndex_t idx = m_Lookup.NextInorder(index); + if (idx == m_Lookup.InvalidIndex()) + return INVALID_LOCALIZE_STRING_INDEX; + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the name of the localization string by index +//----------------------------------------------------------------------------- +const char *CLocalizedStringTable::GetNameByIndex(StringIndex_t index) +{ + localizedstring_t &lstr = m_Lookup[index]; + return &m_Names[lstr.nameIndex]; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the localized string value by index +//----------------------------------------------------------------------------- +wchar_t *CLocalizedStringTable::GetValueByIndex(StringIndex_t index) +{ + if (index == INVALID_LOCALIZE_STRING_INDEX) + return NULL; + + localizedstring_t &lstr = m_Lookup[index]; + return &m_Values[lstr.valueIndex]; +} + + +CLocalizedStringTable *CLocalizedStringTable::s_pTable = NULL; + +bool CLocalizedStringTable::FastValueLessFunc( const fastvalue_t& lhs, const fastvalue_t& rhs ) +{ + Assert( s_pTable ); + + const wchar_t *w1 = lhs.search ? lhs.search : &s_pTable->m_Values[ lhs.valueindex ]; + const wchar_t *w2 = rhs.search ? rhs.search : &s_pTable->m_Values[ rhs.valueindex ]; + + return ( wcscmp( w1, w2 ) < 0 ) ? true : false; +} + +void CLocalizedStringTable::BuildFastValueLookup() +{ + m_FastValueLookup.RemoveAll(); + s_pTable = this; + + // Build it + int c = m_Lookup.Count(); + for ( int i = 0; i < c; ++i ) + { + fastvalue_t val; + val.valueindex = m_Lookup[ i ].valueIndex; + val.search = NULL; + + m_FastValueLookup.Insert( val ); + } +} + +void CLocalizedStringTable::DiscardFastValueLookup() +{ + m_FastValueLookup.RemoveAll(); + s_pTable = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CLocalizedStringTable::FindExistingValueIndex( const wchar_t *value ) +{ + if ( !s_pTable ) + return INVALID_LOCALIZE_STRING_INDEX; + + fastvalue_t val; + val.valueindex = -1; + val.search = value; + + int idx = m_FastValueLookup.Find( val ); + if ( idx != m_FastValueLookup.InvalidIndex() ) + { + return m_FastValueLookup[ idx ].valueindex; + } + return INVALID_LOCALIZE_STRING_INDEX; +} + +//----------------------------------------------------------------------------- +// Purpose: returns which file a string was loaded from +//----------------------------------------------------------------------------- +const char *CLocalizedStringTable::GetFileNameByIndex(StringIndex_t index) +{ + localizedstring_t &lstr = m_Lookup[index]; + return lstr.filename.String(); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the value in the index +//----------------------------------------------------------------------------- +void CLocalizedStringTable::SetValueByIndex(StringIndex_t index, wchar_t *newValue) +{ + // get the existing string + localizedstring_t &lstr = m_Lookup[index]; + wchar_t *wstr = &m_Values[lstr.valueIndex]; + + // see if the new string will fit within the old memory + int newLen = wcslen(newValue); + int oldLen = wcslen(wstr); + + if (newLen > oldLen) + { + // it won't fit, so allocate new memory - this is wasteful, but only happens in edit mode + lstr.valueIndex = m_Values.AddMultipleToTail(newLen + 1); + memcpy(&m_Values[lstr.valueIndex], newValue, (newLen + 1) * sizeof(wchar_t)); + } + else + { + // copy the string into the old position + wcscpy(wstr, newValue); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns number of localization files currently loaded +//----------------------------------------------------------------------------- +int CLocalizedStringTable::GetLocalizationFileCount() +{ + return m_LocalizationFiles.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns localization filename by index +//----------------------------------------------------------------------------- +const char *CLocalizedStringTable::GetLocalizationFileName(int index) +{ + return m_LocalizationFiles[index].symName.String(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns whether a localization file has been loaded already +//----------------------------------------------------------------------------- +bool CLocalizedStringTable::LocalizationFileIsLoaded(const char *name) +{ + int c = m_LocalizationFiles.Count(); + for ( int i = 0; i < c; ++i ) + { + if ( !Q_stricmp( m_LocalizationFiles[ i ].symName.String(), name ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructs a string, inserting variables where necessary +//----------------------------------------------------------------------------- +void CLocalizedStringTable::ConstructString(wchar_t *unicodeOutput, int unicodeBufferSizeInBytes, const char *tokenName, KeyValues *localizationVariables) +{ + StringIndex_t index = FindIndex(tokenName); + + if (index != INVALID_LOCALIZE_STRING_INDEX) + { + ConstructString(unicodeOutput, unicodeBufferSizeInBytes, index, localizationVariables); + } + else + { + // string not found, just return the token name + ConvertANSIToUnicode(tokenName, unicodeOutput, unicodeBufferSizeInBytes); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Constructs a string, inserting variables where necessary +//----------------------------------------------------------------------------- +void CLocalizedStringTable::ConstructString(wchar_t *unicodeOutput, int unicodeBufferSizeInBytes, StringIndex_t unlocalizedTextSymbol, KeyValues *localizationVariables) +{ + if (unicodeBufferSizeInBytes < 1) + return; + + unicodeOutput[0] = 0; + const wchar_t *searchPos = GetValueByIndex(unlocalizedTextSymbol); + if (!searchPos) + { + wcsncpy(unicodeOutput, L"[unknown string]", unicodeBufferSizeInBytes / sizeof(wchar_t)); + return; + } + + ILocalize::ConstructString( unicodeOutput, unicodeBufferSizeInBytes, searchPos, localizationVariables ); +} diff --git a/vgui2/src/MemoryBitmap.cpp b/vgui2/src/MemoryBitmap.cpp new file mode 100644 index 0000000..d6c585b --- /dev/null +++ b/vgui2/src/MemoryBitmap.cpp @@ -0,0 +1,174 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#if !defined(_STATIC_LINKED) || defined(_VGUI_DLL) + +#include <vgui/ISurface.h> + +#include "Memorybitmap.h" +#include "vgui_internal.h" + +#include <string.h> +#include <stdlib.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : *filename - image file to load +//----------------------------------------------------------------------------- +MemoryBitmap::MemoryBitmap(unsigned char *texture,int wide, int tall) +{ + _texture=texture; + _id = 0; + _uploaded = false; + _color = Color(255, 255, 255, 255); + _pos[0] = _pos[1] = 0; + _valid = true; + _w = wide; + _h = tall; + ForceUpload(texture,wide,tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +MemoryBitmap::~MemoryBitmap() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void MemoryBitmap::GetSize(int &wide, int &tall) +{ + wide = 0; + tall = 0; + + if (!_valid) + return; + + g_pSurface->DrawGetTextureSize(_id, wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: size of the bitmap +//----------------------------------------------------------------------------- +void MemoryBitmap::GetContentSize(int &wide, int &tall) +{ + GetSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: ignored +//----------------------------------------------------------------------------- +void MemoryBitmap::SetSize(int x, int y) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void MemoryBitmap::SetPos(int x, int y) +{ + _pos[0] = x; + _pos[1] = y; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void MemoryBitmap::SetColor(Color col) +{ + _color = col; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the file name of the bitmap +//----------------------------------------------------------------------------- +const char *MemoryBitmap::GetName() +{ + return "MemoryBitmap"; +} + + +//----------------------------------------------------------------------------- +// Purpose: Renders the loaded image, uploading it if necessary +// Assumes a valid image is always returned from uploading +//----------------------------------------------------------------------------- +void MemoryBitmap::Paint() +{ + if (!_valid) + return; + + // if we don't have an _id then lets make one + if (!_id) + { + _id = g_pSurface->CreateNewTextureID( true ); + } + + // if we have not uploaded yet, lets go ahead and do so + if (!_uploaded) + { + ForceUpload(_texture,_w,_h); + } + + //set the texture current, set the color, and draw the biatch + g_pSurface->DrawSetTexture(_id); + g_pSurface->DrawSetColor(_color[0], _color[1], _color[2], _color[3]); + + int wide, tall; + GetSize(wide, tall); + g_pSurface->DrawTexturedRect(_pos[0], _pos[1], _pos[0] + wide, _pos[1] + tall); +} + + +//----------------------------------------------------------------------------- +// Purpose: ensures the bitmap has been uploaded +//----------------------------------------------------------------------------- +void MemoryBitmap::ForceUpload(unsigned char *texture,int wide, int tall) +{ + _texture=texture; + _w = wide; + _h = tall; + + if (!_valid) + return; + +// if (_uploaded) +// return; + + if(_w==0 || _h==0) + return; + + if (!_id) + { + _id = g_pSurface->CreateNewTextureID( true ); + } +/* drawSetTextureRGBA(IE->textureID,static_cast<const char *>(lpvBits), w, h); +*/ + g_pSurface->DrawSetTextureRGBA(_id, _texture, _w, _h, false, true); + _uploaded = true; + + _valid = g_pSurface->IsTextureIDValid(_id); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +HTexture MemoryBitmap::GetID() +{ + return _id; +} + +#endif // _STATIC_LINKED && _VGUI_DLL + diff --git a/vgui2/src/Memorybitmap.h b/vgui2/src/Memorybitmap.h new file mode 100644 index 0000000..10606a6 --- /dev/null +++ b/vgui2/src/Memorybitmap.h @@ -0,0 +1,67 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MEMORYBITMAP_H +#define MEMORYBITMAP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui/IImage.h> +#include <Color.h> + +namespace vgui +{ + +typedef unsigned long HTexture; + +//----------------------------------------------------------------------------- +// Purpose: Holds a single image created from a chunk of memory, internal to vgui only +//----------------------------------------------------------------------------- +class MemoryBitmap: public IImage +{ +public: + MemoryBitmap(unsigned char *texture,int wide, int tall); + ~MemoryBitmap(); + + // IImage implementation + virtual void Paint(); + virtual void GetSize(int &wide, int &tall); + virtual void GetContentSize(int &wide, int &tall); + virtual void SetPos(int x, int y); + virtual void SetSize(int x, int y); + virtual void SetColor(Color col); + + virtual bool Evict() { return false; } + virtual int GetNumFrames() { return 0; } + virtual void SetFrame( int nFrame ) {} + virtual HTexture GetID(); // returns the texture id + virtual void SetRotation( int iRotation ) { return; }; + + // methods + void ForceUpload(unsigned char *texture,int wide, int tall); // ensures the bitmap has been uploaded + const char *GetName(); + bool IsValid() + { + return _valid; + } + +private: + HTexture _id; + bool _uploaded; + bool _valid; + unsigned char *_texture; + int _pos[2]; + Color _color; + int _w,_h; // size of the texture +}; + +} // namespace vgui + +#endif // MEMORYBITMAP_H diff --git a/vgui2/src/MessageListener.cpp b/vgui2/src/MessageListener.cpp new file mode 100644 index 0000000..17b468a --- /dev/null +++ b/vgui2/src/MessageListener.cpp @@ -0,0 +1,112 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "IMessageListener.h" +#include "VPanel.h" +#include "vgui_internal.h" + +#include <KeyValues.h> +#include "vgui/IClientPanel.h" +#include "vgui/IVGui.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Implementation of the message listener +//----------------------------------------------------------------------------- +class CMessageListener : public IMessageListener +{ +public: + virtual void Message( VPanel* pSender, VPanel* pReceiver, KeyValues* pKeyValues, MessageSendType_t type ); +}; + +void CMessageListener::Message( VPanel* pSender, VPanel* pReceiver, KeyValues* pKeyValues, MessageSendType_t type ) +{ + char const *pSenderName = "NULL"; + if (pSender) + pSenderName = pSender->Client()->GetName(); + + char const *pSenderClass = "NULL"; + if (pSender) + pSenderClass = pSender->Client()->GetClassName(); + + char const *pReceiverName = "unknown name"; + if (pReceiver) + pReceiverName = pReceiver->Client()->GetName(); + + char const *pReceiverClass = "unknown class"; + if (pReceiver) + pReceiverClass = pReceiver->Client()->GetClassName(); + + // FIXME: Make a bunch of filters here + + // filter out key focus messages + if (!strcmp (pKeyValues->GetName(), "KeyFocusTicked")) + { + return; + } + // filter out mousefocus messages + else if (!strcmp (pKeyValues->GetName(), "MouseFocusTicked")) + { + return; + } + // filter out cursor movement messages + else if (!strcmp (pKeyValues->GetName(), "CursorMoved")) + { + return; + } + // filter out cursor entered messages + else if (!strcmp (pKeyValues->GetName(), "CursorEntered")) + { + return; + } + // filter out cursor exited messages + else if (!strcmp (pKeyValues->GetName(), "CursorExited")) + { + return; + } + // filter out MouseCaptureLost messages + else if (!strcmp (pKeyValues->GetName(), "MouseCaptureLost")) + { + return; + } + // filter out MousePressed messages + else if (!strcmp (pKeyValues->GetName(), "MousePressed")) + { + return; + } + // filter out MouseReleased messages + else if (!strcmp (pKeyValues->GetName(), "MouseReleased")) + { + return; + } + // filter out Tick messages + else if (!strcmp (pKeyValues->GetName(), "Tick")) + { + return; + } + + Msg( "%s : (%s (%s) - > %s (%s)) )\n", + pKeyValues->GetName(), pSenderClass, pSenderName, pReceiverClass, pReceiverName ); + +} + + +//----------------------------------------------------------------------------- +// Singleton instance +//----------------------------------------------------------------------------- +static CMessageListener s_MessageListener; +IMessageListener *MessageListener() +{ + return &s_MessageListener; +} + +}
\ No newline at end of file diff --git a/vgui2/src/ScalableImageBorder.cpp b/vgui2/src/ScalableImageBorder.cpp new file mode 100644 index 0000000..bfa5606 --- /dev/null +++ b/vgui2/src/ScalableImageBorder.cpp @@ -0,0 +1,272 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <stdio.h> +#include <string.h> + +#include <vgui_controls/Panel.h> +#include "vgui/IPanel.h" +#include "vgui/IScheme.h" +#include "vgui/ISurface.h" + +#include "vgui_internal.h" +#include "ScalableImageBorder.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +ScalableImageBorder::ScalableImageBorder() +{ + _inset[0]=0; + _inset[1]=0; + _inset[2]=0; + _inset[3]=0; + _name = NULL; + m_eBackgroundType = IBorder::BACKGROUND_TEXTURED; + + m_iSrcCornerHeight = 0; + m_iSrcCornerWidth = 0; + m_iCornerHeight = 0; + m_iCornerWidth = 0; + m_pszImageName = NULL; + m_iTextureID = g_pSurface->CreateNewTextureID(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +ScalableImageBorder::~ScalableImageBorder() +{ + if ( vgui::surface() && m_iTextureID != -1 ) + { + vgui::surface()->DestroyTextureID( m_iTextureID ); + m_iTextureID = -1; + } + + delete [] _name; + if ( m_pszImageName ) + { + delete [] m_pszImageName; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImageBorder::SetImage(const char *imageName) +{ + if ( m_pszImageName ) + { + delete [] m_pszImageName; + m_pszImageName = NULL; + } + + if (*imageName) + { + int len = Q_strlen(imageName) + 1 + 5; // 5 for "vgui/" + delete [] m_pszImageName; + m_pszImageName = new char[ len ]; + Q_snprintf( m_pszImageName, len, "vgui/%s", imageName ); + + g_pSurface->DrawSetTextureFile( m_iTextureID, m_pszImageName, true, false); + + // get image dimensions, compare to m_iSrcCornerHeight, m_iSrcCornerWidth + int wide,tall; + g_pSurface->DrawGetTextureSize( m_iTextureID, wide, tall ); + + m_flCornerWidthPercent = ( wide > 0 ) ? ( (float)m_iSrcCornerWidth / (float)wide ) : 0; + m_flCornerHeightPercent = ( tall > 0 ) ? ( (float)m_iSrcCornerHeight / (float)tall ) : 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImageBorder::SetInset(int left,int top,int right,int bottom) +{ + _inset[SIDE_LEFT] = left; + _inset[SIDE_TOP] = top; + _inset[SIDE_RIGHT] = right; + _inset[SIDE_BOTTOM] = bottom; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImageBorder::GetInset(int& left,int& top,int& right,int& bottom) +{ + left = _inset[SIDE_LEFT]; + top = _inset[SIDE_TOP]; + right = _inset[SIDE_RIGHT]; + bottom = _inset[SIDE_BOTTOM]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImageBorder::Paint(int x, int y, int wide, int tall) +{ + Paint(x, y, wide, tall, -1, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the border with the specified size +//----------------------------------------------------------------------------- +void ScalableImageBorder::Paint(int x, int y, int wide, int tall, int breakSide, int breakStart, int breakEnd) +{ + if ( !m_pszImageName || !m_pszImageName[0] ) + return; + + g_pSurface->DrawSetColor( m_Color ); + g_pSurface->DrawSetTexture( m_iTextureID ); + + float uvx = 0; + float uvy = 0; + float uvw, uvh; + + float drawW, drawH; + + int row, col; + for ( row=0;row<3;row++ ) + { + x = 0; + uvx = 0; + + if ( row == 0 || row == 2 ) + { + //uvh - row 0 or 2, is src_corner_height + uvh = m_flCornerHeightPercent; + drawH = m_iCornerHeight; + } + else + { + //uvh - row 1, is tall - ( 2 * src_corner_height ) ( min 0 ) + uvh = max( 1.f - 2.f * m_flCornerHeightPercent, 0.0f ); + drawH = max( 0, ( tall - 2 * m_iCornerHeight ) ); + } + + for ( col=0;col<3;col++ ) + { + if ( col == 0 || col == 2 ) + { + //uvw - col 0 or 2, is src_corner_width + uvw = m_flCornerWidthPercent; + drawW = m_iCornerWidth; + } + else + { + //uvw - col 1, is wide - ( 2 * src_corner_width ) ( min 0 ) + uvw = max( 1.f - 2.f * m_flCornerWidthPercent, 0.0f ); + drawW = max( 0, ( wide - 2 * m_iCornerWidth ) ); + } + + Vector2D uv11( uvx, uvy ); + Vector2D uv21( uvx+uvw, uvy ); + Vector2D uv22( uvx+uvw, uvy+uvh ); + Vector2D uv12( uvx, uvy+uvh ); + + vgui::Vertex_t verts[4]; + verts[0].Init( Vector2D( x, y ), uv11 ); + verts[1].Init( Vector2D( x+drawW, y ), uv21 ); + verts[2].Init( Vector2D( x+drawW, y+drawH ), uv22 ); + verts[3].Init( Vector2D( x, y+drawH ), uv12 ); + + g_pSurface->DrawTexturedPolygon( 4, verts ); + + x += drawW; + uvx += uvw; + } + + y += drawH; + uvy += uvh; + } + + g_pSurface->DrawSetTexture(0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImageBorder::Paint(VPANEL panel) +{ + // get panel size + int wide, tall; + ipanel()->GetSize( panel, wide, tall ); + Paint(0, 0, wide, tall, -1, 0, 0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ScalableImageBorder::ApplySchemeSettings(IScheme *pScheme, KeyValues *inResourceData) +{ + m_eBackgroundType = (backgroundtype_e)inResourceData->GetInt("backgroundtype"); + + m_iSrcCornerHeight = inResourceData->GetInt( "src_corner_height" ); + m_iSrcCornerWidth = inResourceData->GetInt( "src_corner_width" ); + m_iCornerHeight = inResourceData->GetInt( "draw_corner_height" ); + m_iCornerWidth = inResourceData->GetInt( "draw_corner_width" ); + + // scale the x and y up to our screen co-ords + m_iCornerHeight = scheme()->GetProportionalScaledValue( m_iCornerHeight); + m_iCornerWidth = scheme()->GetProportionalScaledValue(m_iCornerWidth); + + const char *imageName = inResourceData->GetString("image", ""); + SetImage( imageName ); + + m_bPaintFirst = inResourceData->GetInt("paintfirst", true ); + + const char *col = inResourceData->GetString("color", NULL); + if ( col && col[0] ) + { + m_Color = pScheme->GetColor(col, Color(255, 255, 255, 255)); + } + else + { + m_Color = Color(255, 255, 255, 255); + } +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *ScalableImageBorder::GetName() +{ + if (_name) + return _name; + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void ScalableImageBorder::SetName(const char *name) +{ + if (_name) + { + delete [] _name; + } + + int len = Q_strlen(name) + 1; + _name = new char[ len ]; + Q_strncpy( _name, name, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBorder::backgroundtype_e ScalableImageBorder::GetBackgroundType() +{ + return m_eBackgroundType; +} + diff --git a/vgui2/src/ScalableImageBorder.h b/vgui2/src/ScalableImageBorder.h new file mode 100644 index 0000000..d1df631 --- /dev/null +++ b/vgui2/src/ScalableImageBorder.h @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Core implementation of vgui +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SCALABLE_IMAGE_BORDER_H +#define SCALABLE_IMAGE_BORDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui/IBorder.h> +#include <vgui/IScheme.h> +#include <vgui/IPanel.h> +#include <Color.h> + +class KeyValues; + +//----------------------------------------------------------------------------- +// Purpose: Custom border that renders itself with images +//----------------------------------------------------------------------------- +class ScalableImageBorder : public vgui::IBorder +{ +public: + ScalableImageBorder(); + ~ScalableImageBorder(); + + virtual void Paint(vgui::VPANEL panel); + virtual void Paint(int x0, int y0, int x1, int y1); + virtual void Paint(int x0, int y0, int x1, int y1, int breakSide, int breakStart, int breakStop); + virtual void SetInset(int left, int top, int right, int bottom); + virtual void GetInset(int &left, int &top, int &right, int &bottom); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme, KeyValues *inResourceData); + + virtual const char *GetName(); + virtual void SetName(const char *name); + virtual backgroundtype_e GetBackgroundType(); + + virtual bool PaintFirst( void ) { return m_bPaintFirst; } + +protected: + void SetImage(const char *imageName); + +protected: + int _inset[4]; + +private: + // protected copy constructor to prevent use + ScalableImageBorder(ScalableImageBorder&); + + char *_name; + + backgroundtype_e m_eBackgroundType; + + friend class VPanel; + + int m_iSrcCornerHeight; // in pixels, how tall is the corner inside the image + int m_iSrcCornerWidth; // same for width + int m_iCornerHeight; // output size of the corner height in pixels + int m_iCornerWidth; // same for width + + int m_iTextureID; + + float m_flCornerWidthPercent; // corner width as percentage of image width + float m_flCornerHeightPercent; // same for height + + char *m_pszImageName; + bool m_bPaintFirst; + + Color m_Color; +}; + +#endif // SCALABLE_IMAGE_BORDER_H diff --git a/vgui2/src/Scheme.cpp b/vgui2/src/Scheme.cpp new file mode 100644 index 0000000..f41a695 --- /dev/null +++ b/vgui2/src/Scheme.cpp @@ -0,0 +1,1532 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <stdio.h> +#include <math.h> + +#include <vgui/VGUI.h> +#include <vgui/IScheme.h> +#include <KeyValues.h> +#include <vgui/ISurface.h> +#include <vgui/IPanel.h> +#include <vgui/ISystem.h> +#include <vstdlib/IKeyValuesSystem.h> +#include <vgui/IVGui.h> + +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" +#include "tier1/utldict.h" +#include "VGUI_Border.h" +#include "ScalableImageBorder.h" +#include "ImageBorder.h" +#include "vgui_internal.h" +#include "bitmap.h" +#include "filesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; +#define FONT_ALIAS_NAME_LENGTH 64 + +//----------------------------------------------------------------------------- +// Purpose: Implementation of global scheme interface +//----------------------------------------------------------------------------- +class CScheme : public IScheme +{ +public: + CScheme(); + + // gets a string from the default settings section + virtual const char *GetResourceString(const char *stringName); + + // returns a pointer to an existing border + virtual IBorder *GetBorder(const char *borderName); + + // returns a pointer to an existing font + virtual HFont GetFont(const char *fontName, bool proportional); + + // m_pkvColors + virtual Color GetColor( const char *colorName, Color defaultColor); + + + void Shutdown( bool full ); + void LoadFromFile( VPANEL sizingPanel, const char *filename, const char *tag, KeyValues *inKeys ); + + // Gets at the scheme's name + const char *GetName() { return tag; } + const char *GetFileName() { return fileName; } + + char const *GetFontName( const HFont& font ); + + void ReloadFontGlyphs(); + + VPANEL GetSizingPanel() { return m_SizingPanel; } + + void SpewFonts(); + + bool GetFontRange( const char *fontname, int &nMin, int &nMax ); + void SetFontRange( const char *fontname, int nMin, int nMax ); + + // Get the number of borders + virtual int GetBorderCount() const; + + // Get the border at the given index + virtual IBorder *GetBorderAtIndex( int iIndex ); + + // Get the number of fonts + virtual int GetFontCount() const; + + // Get the font at the given index + virtual HFont GetFontAtIndex( int iIndex ); + + // Get color data + virtual const KeyValues *GetColorData() const; + +private: + const char *LookupSchemeSetting(const char *pchSetting); + const char *GetMungedFontName( const char *fontName, const char *scheme, bool proportional); + void LoadFonts(); + void LoadBorders(); + HFont FindFontInAliasList( const char *fontName ); + int GetMinimumFontHeightForCurrentLanguage(); + + char fileName[256]; + char tag[64]; + + KeyValues *m_pData; + KeyValues *m_pkvBaseSettings; + KeyValues *m_pkvColors; + int m_nColorCount; + + struct SchemeBorder_t + { + IBorder *border; + int borderSymbol; + bool bSharedBorder; + }; + CUtlVector<SchemeBorder_t> m_BorderList; + IBorder *m_pBaseBorder; // default border to use if others not found + KeyValues *m_pkvBorders; +#pragma pack(1) + struct fontalias_t + { + CUtlSymbol _trueFontName; + unsigned short _font : 15; + unsigned short m_bProportional : 1; + }; +#pragma pack() + friend struct fontalias_t; + + CUtlDict< fontalias_t, int > m_FontAliases; + VPANEL m_SizingPanel; + int m_nScreenWide; + int m_nScreenTall; + + struct fontrange_t + { + int _min; + int _max; + }; + CUtlDict< fontrange_t, int > m_FontRanges; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Implementation of global scheme interface +//----------------------------------------------------------------------------- +class CSchemeManager : public ISchemeManager +{ +public: + CSchemeManager(); + ~CSchemeManager(); + + // loads a scheme from a file + // first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that + // tag is friendly string representing the name of the loaded scheme + virtual HScheme LoadSchemeFromFile(const char *fileName, const char *tag); + // first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that + virtual HScheme LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag); + + // reloads the schemes from the file + virtual void ReloadSchemes(); + virtual void ReloadFonts(); + + // returns a handle to the default (first loaded) scheme + virtual HScheme GetDefaultScheme(); + + // returns a handle to the scheme identified by "tag" + virtual HScheme GetScheme(const char *tag); + + // returns a pointer to an image + virtual IImage *GetImage(const char *imageName, bool hardwareFiltered); + virtual HTexture GetImageID(const char *imageName, bool hardwareFiltered); + + virtual IScheme *GetIScheme( HScheme scheme ); + + virtual void Shutdown( bool full ); + + // gets the proportional coordinates for doing screen-size independant panel layouts + // use these for font, image and panel size scaling (they all use the pixel height of the display for scaling) + virtual int GetProportionalScaledValue(int normalizedValue); + virtual int GetProportionalNormalizedValue(int scaledValue); + + // gets the proportional coordinates for doing screen-size independant panel layouts + // use these for font, image and panel size scaling (they all use the pixel height of the display for scaling) + virtual int GetProportionalScaledValueEx( HScheme scheme, int normalizedValue ); + virtual int GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue ); + + virtual bool DeleteImage( const char *pImageName ); + + // gets the proportional coordinates for doing screen-size independant panel layouts + // use these for font, image and panel size scaling (they all use the pixel height of the display for scaling) + int GetProportionalScaledValueEx( CScheme *pScheme, int normalizedValue ); + int GetProportionalNormalizedValueEx( CScheme *pScheme, int scaledValue ); + + void SpewFonts(); + +private: + + int GetProportionalScaledValue_( int rootWide, int rootTall, int normalizedValue ); + int GetProportionalNormalizedValue_( int rootWide, int rootTall, int scaledValue ); + + // Search for already-loaded schemes + HScheme FindLoadedScheme(const char *fileName); + + CUtlVector<CScheme *> m_Schemes; + + static const char *s_pszSearchString; + struct CachedBitmapHandle_t + { + Bitmap *pBitmap; + }; + static bool BitmapHandleSearchFunc(const CachedBitmapHandle_t &, const CachedBitmapHandle_t &); + CUtlRBTree<CachedBitmapHandle_t, int> m_Bitmaps; +}; + +const char *CSchemeManager::s_pszSearchString = NULL; + +//----------------------------------------------------------------------------- +// Purpose: search function for stored bitmaps +//----------------------------------------------------------------------------- +bool CSchemeManager::BitmapHandleSearchFunc(const CachedBitmapHandle_t &lhs, const CachedBitmapHandle_t &rhs) +{ + // a NULL bitmap indicates to use the search string instead + if (lhs.pBitmap && rhs.pBitmap) + { + return stricmp(lhs.pBitmap->GetName(), rhs.pBitmap->GetName()) > 0; + } + else if (lhs.pBitmap) + { + return stricmp(lhs.pBitmap->GetName(), s_pszSearchString) > 0; + } + return stricmp(s_pszSearchString, rhs.pBitmap->GetName()) > 0; +} + + + +CSchemeManager g_Scheme; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CSchemeManager, ISchemeManager, VGUI_SCHEME_INTERFACE_VERSION, g_Scheme); + + +namespace vgui +{ +vgui::ISchemeManager *g_pScheme = &g_Scheme; +} // namespace vgui + +CON_COMMAND( vgui_spew_fonts, "" ) +{ + g_Scheme.SpewFonts(); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSchemeManager::CSchemeManager() +{ + // 0th element is null, since that would be an invalid handle + CScheme *nullScheme = new CScheme(); + m_Schemes.AddToTail(nullScheme); + m_Bitmaps.SetLessFunc(&BitmapHandleSearchFunc); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSchemeManager::~CSchemeManager() +{ + int i; + for ( i = 0; i < m_Schemes.Count(); i++ ) + { + delete m_Schemes[i]; + } + m_Schemes.RemoveAll(); + + for ( i = 0; i < m_Bitmaps.MaxElement(); i++ ) + { + if (m_Bitmaps.IsValidIndex(i)) + { + delete m_Bitmaps[i].pBitmap; + } + } + m_Bitmaps.RemoveAll(); + + Shutdown( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: Reload the fonts in all schemes +//----------------------------------------------------------------------------- +void CSchemeManager::ReloadFonts() +{ + for (int i = 1; i < m_Schemes.Count(); i++) + { + m_Schemes[i]->ReloadFontGlyphs(); + } +} + +//----------------------------------------------------------------------------- +// Converts the handle into an interface +//----------------------------------------------------------------------------- +IScheme *CSchemeManager::GetIScheme( HScheme scheme ) +{ + if ( scheme >= (unsigned long)m_Schemes.Count() ) + { + AssertOnce( !"Invalid scheme requested." ); + return NULL; + } + else + { + return m_Schemes[scheme]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSchemeManager::Shutdown( bool full ) +{ + // Full shutdown kills the null scheme + for( int i = full ? 0 : 1; i < m_Schemes.Count(); i++ ) + { + m_Schemes[i]->Shutdown( full ); + } + + if ( full ) + { + m_Schemes.RemoveAll(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: loads a scheme from disk +//----------------------------------------------------------------------------- +HScheme CSchemeManager::FindLoadedScheme(const char *fileName) +{ + // Find the scheme in the list of already loaded schemes + for (int i = 1; i < m_Schemes.Count(); i++) + { + char const *schemeFileName = m_Schemes[i]->GetFileName(); + if (!stricmp(schemeFileName, fileName)) + return i; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CScheme::CScheme() +{ + fileName[ 0 ] = 0; + tag[ 0 ] = 0; + + m_pData = NULL; + m_pkvBaseSettings = NULL; + m_pkvColors = NULL; + m_nColorCount = 0; + + m_pBaseBorder = NULL; // default border to use if others not found + m_pkvBorders = NULL; + m_SizingPanel = 0; + m_nScreenWide = -1; + m_nScreenTall = -1; +} + +// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that +HScheme CSchemeManager::LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag) +{ + // Look to see if we've already got this scheme... + HScheme hScheme = FindLoadedScheme(fileName); + if (hScheme != 0) + { + CScheme *pScheme = static_cast< CScheme * >( GetIScheme( hScheme ) ); + if ( IsPC() && pScheme ) + { + pScheme->ReloadFontGlyphs(); + } + return hScheme; + } + + KeyValues *data; + data = new KeyValues("Scheme"); + + data->UsesEscapeSequences( true ); // VGUI uses this + + // Look first in game directory + bool result = data->LoadFromFile( g_pFullFileSystem, fileName, "GAME" ); + if ( !result ) + { + // look in any directory + result = data->LoadFromFile( g_pFullFileSystem, fileName, NULL ); + } + + if (!result) + { + data->deleteThis(); + return 0; + } + + if ( IsX360() ) + { + data->ProcessResolutionKeys( g_pSurface->GetResolutionKey() ); + } + if ( IsPC() ) + { + ConVarRef cl_hud_minmode( "cl_hud_minmode", true ); + if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() ) + { + data->ProcessResolutionKeys( "_minmode" ); + } + } + if( g_pIVgui->GetVRMode() ) + { + data->ProcessResolutionKeys( "_vrmode" ); + } + + CScheme *newScheme = new CScheme(); + newScheme->LoadFromFile( sizingPanel, fileName, tag, data ); + + return m_Schemes.AddToTail(newScheme); +} + +//----------------------------------------------------------------------------- +// Purpose: loads a scheme from disk +//----------------------------------------------------------------------------- +HScheme CSchemeManager::LoadSchemeFromFile(const char *fileName, const char *tag) +{ + return LoadSchemeFromFileEx( 0, fileName, tag ); +} + +//----------------------------------------------------------------------------- +// Purpose: Table of scheme file entries, translation from old goldsrc schemes to new src schemes +//----------------------------------------------------------------------------- +struct SchemeEntryTranslation_t +{ + const char *pchNewEntry; + const char *pchOldEntry; + const char *pchDefaultValue; +}; +SchemeEntryTranslation_t g_SchemeTranslation[] = +{ + { "Border.Bright", "BorderBright", "200 200 200 196" }, // the lit side of a control + { "Border.Dark" "BorderDark", "40 40 40 196" }, // the dark/unlit side of a control + { "Border.Selection" "BorderSelection", "0 0 0 196" }, // the additional border color for displaying the default/selected button + + { "Button.TextColor", "ControlFG", "White" }, + { "Button.BgColor", "ControlBG", "Blank" }, + { "Button.ArmedTextColor", "ControlFG" }, + { "Button.ArmedBgColor", "ControlBG" }, + { "Button.DepressedTextColor", "ControlFG" }, + { "Button.DepressedBgColor", "ControlBG" }, + { "Button.FocusBorderColor", "0 0 0 255" }, + + { "CheckButton.TextColor", "BaseText" }, + { "CheckButton.SelectedTextColor", "BrightControlText" }, + { "CheckButton.BgColor", "CheckBgColor" }, + { "CheckButton.Border1", "CheckButtonBorder1" }, + { "CheckButton.Border2", "CheckButtonBorder2" }, + { "CheckButton.Check", "CheckButtonCheck" }, + + { "ComboBoxButton.ArrowColor", "LabelDimText" }, + { "ComboBoxButton.ArmedArrowColor", "MenuButton/ArmedArrowColor" }, + { "ComboBoxButton.BgColor", "MenuButton/ButtonBgColor" }, + { "ComboBoxButton.DisabledBgColor", "ControlBG" }, + + { "Frame.TitleTextInsetX", NULL, "32" }, + { "Frame.ClientInsetX", NULL, "8" }, + { "Frame.ClientInsetY", NULL, "6" }, + { "Frame.BgColor", "BgColor" }, + { "Frame.OutOfFocusBgColor", "BgColor" }, + { "Frame.FocusTransitionEffectTime",NULL, "0" }, + { "Frame.TransitionEffectTime", NULL, "0" }, + { "Frame.AutoSnapRange", NULL, "8" }, + { "FrameGrip.Color1", "BorderBright" }, + { "FrameGrip.Color2", "BorderSelection" }, + { "FrameTitleButton.FgColor", "TitleButtonFgColor" }, + { "FrameTitleButton.BgColor", "TitleButtonBgColor" }, + { "FrameTitleButton.DisabledFgColor", "TitleButtonDisabledFgColor" }, + { "FrameTitleButton.DisabledBgColor", "TitleButtonDisabledBgColor" }, + { "FrameSystemButton.FgColor", "TitleBarBgColor" }, + { "FrameSystemButton.BgColor", "TitleBarBgColor" }, + { "FrameSystemButton.Icon", "TitleBarIcon" }, + { "FrameSystemButton.DisabledIcon", "TitleBarDisabledIcon" }, + { "FrameTitleBar.Font", NULL, "Default" }, + { "FrameTitleBar.TextColor", "TitleBarFgColor" }, + { "FrameTitleBar.BgColor", "TitleBarBgColor" }, + { "FrameTitleBar.DisabledTextColor","TitleBarDisabledFgColor" }, + { "FrameTitleBar.DisabledBgColor", "TitleBarDisabledBgColor" }, + + { "GraphPanel.FgColor", "BrightControlText" }, + { "GraphPanel.BgColor", "WindowBgColor" }, + + { "Label.TextDullColor", "LabelDimText" }, + { "Label.TextColor", "BaseText" }, + { "Label.TextBrightColor", "BrightControlText" }, + { "Label.SelectedTextColor", "BrightControlText" }, + { "Label.BgColor", "LabelBgColor" }, + { "Label.DisabledFgColor1", "DisabledFgColor1" }, + { "Label.DisabledFgColor2", "DisabledFgColor2" }, + + { "ListPanel.TextColor", "WindowFgColor" }, + { "ListPanel.TextBgColor", "Menu/ArmedBgColor" }, + { "ListPanel.BgColor", "ListBgColor" }, + { "ListPanel.SelectedTextColor", "ListSelectionFgColor" }, + { "ListPanel.SelectedBgColor", "Menu/ArmedBgColor" }, + { "ListPanel.SelectedOutOfFocusBgColor","SelectionBG2" }, + { "ListPanel.EmptyListInfoTextColor", "LabelDimText" }, + { "ListPanel.DisabledTextColor", "LabelDimText" }, + { "ListPanel.DisabledSelectedTextColor","ListBgColor" }, + + { "Menu.TextColor", "Menu/FgColor" }, + { "Menu.BgColor", "Menu/BgColor" }, + { "Menu.ArmedTextColor", "Menu/ArmedFgColor" }, + { "Menu.ArmedBgColor", "Menu/ArmedBgColor" }, + { "Menu.TextInset", NULL, "6" }, + + { "Panel.FgColor", "FgColor" }, + { "Panel.BgColor", "BgColor" }, + + { "ProgressBar.FgColor", "BrightControlText" }, + { "ProgressBar.BgColor", "WindowBgColor" }, + + { "PropertySheet.TextColor", "FgColorDim" }, + { "PropertySheet.SelectedTextColor", "BrightControlText" }, + { "PropertySheet.TransitionEffectTime", NULL, "0" }, + + { "RadioButton.TextColor", "FgColor" }, + { "RadioButton.SelectedTextColor", "BrightControlText" }, + + { "RichText.TextColor", "WindowFgColor" }, + { "RichText.BgColor", "WindowBgColor" }, + { "RichText.SelectedTextColor", "SelectionFgColor" }, + { "RichText.SelectedBgColor", "SelectionBgColor" }, + + { "ScrollBar.Wide", NULL, "19" }, + + { "ScrollBarButton.FgColor", "DimBaseText" }, + { "ScrollBarButton.BgColor", "ControlBG" }, + { "ScrollBarButton.ArmedFgColor", "BaseText" }, + { "ScrollBarButton.ArmedBgColor", "ControlBG" }, + { "ScrollBarButton.DepressedFgColor", "BaseText" }, + { "ScrollBarButton.DepressedBgColor", "ControlBG" }, + + { "ScrollBarSlider.FgColor", "ScrollBarSlider/ScrollBarSliderFgColor" }, + { "ScrollBarSlider.BgColor", "ScrollBarSlider/ScrollBarSliderBgColor" }, + + { "SectionedListPanel.HeaderTextColor", "SectionTextColor" }, + { "SectionedListPanel.HeaderBgColor", "BuddyListBgColor" }, + { "SectionedListPanel.DividerColor", "SectionDividerColor" }, + { "SectionedListPanel.TextColor", "BuddyButton/FgColor1" }, + { "SectionedListPanel.BrightTextColor", "BuddyButton/ArmedFgColor1" }, + { "SectionedListPanel.BgColor", "BuddyListBgColor" }, + { "SectionedListPanel.SelectedTextColor", "BuddyButton/ArmedFgColor1" }, + { "SectionedListPanel.SelectedBgColor", "BuddyButton/ArmedBgColor" }, + { "SectionedListPanel.OutOfFocusSelectedTextColor", "BuddyButton/ArmedFgColor2" }, + { "SectionedListPanel.OutOfFocusSelectedBgColor", "SelectionBG2" }, + + { "Slider.NobColor", "SliderTickColor" }, + { "Slider.TextColor", "Slider/SliderFgColor" }, + { "Slider.TrackColor", "SliderTrackColor"}, + { "Slider.DisabledTextColor1", "DisabledFgColor1" }, + { "Slider.DisabledTextColor2", "DisabledFgColor2" }, + + { "TextEntry.TextColor", "WindowFgColor" }, + { "TextEntry.BgColor", "WindowBgColor" }, + { "TextEntry.CursorColor", "TextCursorColor" }, + { "TextEntry.DisabledTextColor","WindowDisabledFgColor" }, + { "TextEntry.DisabledBgColor", "ControlBG" }, + { "TextEntry.SelectedTextColor","SelectionFgColor" }, + { "TextEntry.SelectedBgColor", "SelectionBgColor" }, + { "TextEntry.OutOfFocusSelectedBgColor", "SelectionBG2" }, + { "TextEntry.FocusEdgeColor", "BorderSelection" }, + + { "ToggleButton.SelectedTextColor", "BrightControlText" }, + + { "Tooltip.TextColor", "BorderSelection" }, + { "Tooltip.BgColor", "SelectionBG" }, + + { "TreeView.BgColor", "ListBgColor" }, + + { "WizardSubPanel.BgColor", "SubPanelBgColor" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: loads a scheme from from disk into memory +//----------------------------------------------------------------------------- +void CScheme::LoadFromFile( VPANEL sizingPanel, const char *inFilename, const char *inTag, KeyValues *inKeys ) +{ + COM_TimestampedLog( "CScheme::LoadFromFile( %s )", inFilename ); + + Q_strncpy(fileName, inFilename, sizeof(fileName) ); + + m_SizingPanel = sizingPanel; + + m_pData = inKeys; + m_pkvBaseSettings = m_pData->FindKey("BaseSettings", true); + m_pkvColors = m_pData->FindKey("Colors", true); + + // override the scheme name with the tag name + KeyValues *name = m_pData->FindKey("Name", true); + name->SetString("Name", inTag); + + if ( inTag ) + { + Q_strncpy( tag, inTag, sizeof( tag ) ); + } + else + { + Assert( "You need to name the scheme!" ); + Q_strncpy( tag, "default", sizeof( tag ) ); + } + + // translate format from goldsrc scheme to new scheme + for (int i = 0; i < ARRAYSIZE(g_SchemeTranslation); i++) + { + if (!m_pkvBaseSettings->FindKey(g_SchemeTranslation[i].pchNewEntry, false)) + { + const char *pchColor; + + if (g_SchemeTranslation[i].pchOldEntry) + { + pchColor = LookupSchemeSetting(g_SchemeTranslation[i].pchOldEntry); + } + else + { + pchColor = g_SchemeTranslation[i].pchDefaultValue; + } + + Assert( pchColor ); + + m_pkvBaseSettings->SetString(g_SchemeTranslation[i].pchNewEntry, pchColor); + } + } + + // need to copy tag before loading fonts + LoadFonts(); + LoadBorders(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CScheme::GetFontRange( const char *fontname, int &nMin, int &nMax ) +{ + int i = m_FontRanges.Find( fontname ); + if ( i != m_FontRanges.InvalidIndex() ) + { + nMin = m_FontRanges[i]._min; + nMax = m_FontRanges[i]._max; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CScheme::SetFontRange( const char *fontname, int nMin, int nMax ) +{ + int i = m_FontRanges.Find( fontname ); + if ( i != m_FontRanges.InvalidIndex() ) + { + m_FontRanges[i]._min = nMin; + m_FontRanges[i]._max = nMax; + return; + } + + // not already in our list + int iNew = m_FontRanges.Insert( fontname ); + + m_FontRanges[iNew]._min = nMin; + m_FontRanges[iNew]._max = nMax; +} + +//----------------------------------------------------------------------------- +// Purpose: adds all the font specifications to the surface +//----------------------------------------------------------------------------- +void CScheme::LoadFonts() +{ + bool bValid = false; + char language[64]; + memset( language, 0, sizeof( language ) ); + + // get our language + if ( IsPC() ) + { + bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof( language ) - 1 ); + } + else + { + Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) ); + bValid = true; + } + + if ( !bValid ) + { + Q_strncpy( language, "english", sizeof( language ) ); + } + + // add our custom fonts + for (KeyValues *kv = m_pData->FindKey("CustomFontFiles", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + const char *fontFile = kv->GetString(); + if (fontFile && *fontFile) + { + g_pSurface->AddCustomFontFile( NULL, fontFile ); + } + else + { + // we have a block to read + int nRangeMin = 0, nRangeMax = 0; + const char *pszName = NULL; + bool bUseRange = false; + + for ( KeyValues *pData = kv->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) + { + const char *pszKey = pData->GetName(); + if ( !Q_stricmp( pszKey, "font" ) ) + { + fontFile = pData->GetString(); + } + else if ( !Q_stricmp( pszKey, "name" ) ) + { + pszName = pData->GetString(); + } + else + { + // we must have a language + if ( Q_stricmp( language, pszKey ) == 0 ) // matches the language we're running? + { + // get the range + KeyValues *pRange = pData->FindKey( "range" ); + if ( pRange ) + { + bUseRange = true; + sscanf( pRange->GetString(), "%x %x", &nRangeMin, &nRangeMax ); + + if ( nRangeMin > nRangeMax ) + { + int nTemp = nRangeMin; + nRangeMin = nRangeMax; + nRangeMax = nTemp; + } + } + } + } + } + + if ( fontFile && *fontFile ) + { + g_pSurface->AddCustomFontFile( pszName, fontFile ); + + if ( bUseRange ) + { + SetFontRange( pszName, nRangeMin, nRangeMax ); + } + } + } + } + + // add bitmap fonts + for (KeyValues *kv = m_pData->FindKey("BitmapFontFiles", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + const char *fontFile = kv->GetString(); + if (fontFile && *fontFile) + { + bool bSuccess = g_pSurface->AddBitmapFontFile( fontFile ); + if ( bSuccess ) + { + // refer to the font by a user specified symbol + g_pSurface->SetBitmapFontName( kv->GetName(), fontFile ); + } + } + } + + // create the fonts + for (KeyValues *kv = m_pData->FindKey("Fonts", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + for ( int i = 0; i < 2; i++ ) + { + // create the base font + bool proportionalFont = static_cast<bool>( i ); + const char *fontName = GetMungedFontName( kv->GetName(), tag, proportionalFont ); // first time it adds a normal font, and then a proportional one + HFont font = g_pSurface->CreateFont(); + + int j = m_FontAliases.Insert( fontName ); + m_FontAliases[j]._trueFontName = kv->GetName(); + m_FontAliases[j]._font = font; + m_FontAliases[j].m_bProportional = proportionalFont; + } + } + + // load in the font glyphs + ReloadFontGlyphs(); +} + +//----------------------------------------------------------------------------- +// Purpose: Reloads all scheme fonts +//----------------------------------------------------------------------------- +void CScheme::ReloadFontGlyphs() +{ + COM_TimestampedLog( "ReloadFontGlyphs(): Start" ); + + // get our current resolution + if ( m_SizingPanel != 0 ) + { + g_pIPanel->GetSize( m_SizingPanel, m_nScreenWide, m_nScreenTall ); + } + else + { + g_pSurface->GetScreenSize( m_nScreenWide, m_nScreenTall ); + } + + // check our language; some have minimum sizes + int minimumFontHeight = GetMinimumFontHeightForCurrentLanguage(); + + // add the data to all the fonts + KeyValues *fonts = m_pData->FindKey("Fonts", true); + FOR_EACH_DICT_FAST( m_FontAliases, i ) + { + KeyValues *kv = fonts->FindKey( m_FontAliases[i]._trueFontName.String(), true ); + + // walk through creating adding the first matching glyph set to the font + for (KeyValues *fontdata = kv->GetFirstSubKey(); fontdata != NULL; fontdata = fontdata->GetNextKey()) + { + // skip over fonts not meant for this resolution + int fontYResMin = 0, fontYResMax = 0; + sscanf(fontdata->GetString("yres", ""), "%d %d", &fontYResMin, &fontYResMax); + if (fontYResMin) + { + if (!fontYResMax) + { + fontYResMax = fontYResMin; + } + // check the range + if (m_nScreenTall < fontYResMin || m_nScreenTall > fontYResMax) + continue; + } + + int flags = 0; + if (fontdata->GetInt( "italic" )) + { + flags |= ISurface::FONTFLAG_ITALIC; + } + if (fontdata->GetInt( "underline" )) + { + flags |= ISurface::FONTFLAG_UNDERLINE; + } + if (fontdata->GetInt( "strikeout" )) + { + flags |= ISurface::FONTFLAG_STRIKEOUT; + } + if (fontdata->GetInt( "symbol" )) + { + flags |= ISurface::FONTFLAG_SYMBOL; + } + if (fontdata->GetInt( "antialias" ) && g_pSurface->SupportsFeature(ISurface::ANTIALIASED_FONTS)) + { + flags |= ISurface::FONTFLAG_ANTIALIAS; + } + if (fontdata->GetInt( "dropshadow" ) && g_pSurface->SupportsFeature(ISurface::DROPSHADOW_FONTS)) + { + flags |= ISurface::FONTFLAG_DROPSHADOW; + } + if (fontdata->GetInt( "outline" ) && g_pSurface->SupportsFeature(ISurface::OUTLINE_FONTS)) + { + flags |= ISurface::FONTFLAG_OUTLINE; + } + if (fontdata->GetInt( "custom" )) + { + flags |= ISurface::FONTFLAG_CUSTOM; + } + if (fontdata->GetInt( "bitmap" )) + { + flags |= ISurface::FONTFLAG_BITMAP; + } + if (fontdata->GetInt( "rotary" )) + { + flags |= ISurface::FONTFLAG_ROTARY; + } + if (fontdata->GetInt( "additive" )) + { + flags |= ISurface::FONTFLAG_ADDITIVE; + } + + int tall = fontdata->GetInt( "tall" ); + int blur = fontdata->GetInt( "blur" ); + int scanlines = fontdata->GetInt( "scanlines" ); + float scalex = fontdata->GetFloat( "scalex", 1.0f ); + float scaley = fontdata->GetFloat( "scaley", 1.0f ); + + // only grow this font if it doesn't have a resolution filter specified + if ( ( !fontYResMin && !fontYResMax ) && m_FontAliases[i].m_bProportional ) + { + tall = g_Scheme.GetProportionalScaledValueEx( this, tall ); + blur = g_Scheme.GetProportionalScaledValueEx( this, blur ); + scanlines = g_Scheme.GetProportionalScaledValueEx( this, scanlines ); + scalex = g_Scheme.GetProportionalScaledValueEx( this, scalex * 10000.0f ) * 0.0001f; + scaley = g_Scheme.GetProportionalScaledValueEx( this, scaley * 10000.0f ) * 0.0001f; + } + + // clip the font size so that fonts can't be too big + if ( tall > 127 ) + { + tall = 127; + } + + // check our minimum font height + if ( tall < minimumFontHeight ) + { + tall = minimumFontHeight; + } + + if ( flags & ISurface::FONTFLAG_BITMAP ) + { + // add the new set + g_pSurface->SetBitmapFontGlyphSet( + m_FontAliases[i]._font, + g_pSurface->GetBitmapFontName( fontdata->GetString( "name" ) ), + scalex, + scaley, + flags); + } + else + { + int nRangeMin, nRangeMax; + + if ( GetFontRange( fontdata->GetString( "name" ), nRangeMin, nRangeMax ) ) + { + // add the new set + g_pSurface->SetFontGlyphSet( + m_FontAliases[i]._font, + fontdata->GetString( "name" ), + tall, + fontdata->GetInt( "weight" ), + blur, + scanlines, + flags, + nRangeMin, + nRangeMax); + } + else + { + // add the new set + g_pSurface->SetFontGlyphSet( + m_FontAliases[i]._font, + fontdata->GetString( "name" ), + tall, + fontdata->GetInt( "weight" ), + blur, + scanlines, + flags); + } + } + + // don't add any more + break; + } + } + + COM_TimestampedLog( "ReloadFontGlyphs(): End" ); +} + +//----------------------------------------------------------------------------- +// Purpose: create the Border objects from the scheme data +//----------------------------------------------------------------------------- +void CScheme::LoadBorders() +{ + m_pkvBorders = m_pData->FindKey("Borders", true); + {for ( KeyValues *kv = m_pkvBorders->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + if (kv->GetDataType() == KeyValues::TYPE_STRING) + { + // it's referencing another border, ignore for now + } + else + { + int i = m_BorderList.AddToTail(); + + IBorder *border = NULL; + const char *pszBorderType = kv->GetString( "bordertype", NULL ); + if ( pszBorderType && pszBorderType[0] ) + { + if ( !stricmp(pszBorderType,"image") ) + { + border = new ImageBorder(); + } + else if ( !stricmp(pszBorderType,"scalable_image") ) + { + border = new ScalableImageBorder(); + } + else + { + Assert(0); + // Fall back to the base border type. See below. + pszBorderType = NULL; + } + } + + if ( !pszBorderType || !pszBorderType[0] ) + { + border = new Border(); + } + + border->SetName(kv->GetName()); + border->ApplySchemeSettings(this, kv); + + m_BorderList[i].border = border; + m_BorderList[i].bSharedBorder = false; + m_BorderList[i].borderSymbol = kv->GetNameSymbol(); + } + }} + + // iterate again to get the border references + for ( KeyValues *kv = m_pkvBorders->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey()) + { + if (kv->GetDataType() == KeyValues::TYPE_STRING) + { + // it's referencing another border, find it + Border *border = (Border *)GetBorder(kv->GetString()); +// const char *str = kv->GetName(); + Assert(border); + + // add an entry that just references the existing border + int i = m_BorderList.AddToTail(); + m_BorderList[i].border = border; + m_BorderList[i].bSharedBorder = true; + m_BorderList[i].borderSymbol = kv->GetNameSymbol(); + } + } + + m_pBaseBorder = GetBorder("BaseBorder"); +} + +void CScheme::SpewFonts( void ) +{ + Msg( "Scheme: %s (%s)\n", GetName(), GetFileName() ); + FOR_EACH_DICT_FAST( m_FontAliases, i ) + { + const fontalias_t& FontAlias = m_FontAliases[ i ]; + uint32 Font = FontAlias._font; + const char *szFontName = g_pSurface->GetFontName( Font ); + const char *szFontFamilyName = g_pSurface->GetFontFamilyName( Font ); + const char *szTrueFontName = FontAlias._trueFontName.String(); + const char *szFontAlias = m_FontAliases.GetElementName( i ); + + Msg( " %2d: HFont:0x%8.8x, %s, %s, font:%s, tall:%d(%d). %s\n", + i, + Font, + szTrueFontName ? szTrueFontName : "??", + szFontAlias ? szFontAlias : "??", + szFontName ? szFontName : "??", + g_pSurface->GetFontTall( Font ), + g_pSurface->GetFontTallRequested( Font ), + szFontFamilyName ? szFontFamilyName : "" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: reloads the scheme from the file +//----------------------------------------------------------------------------- +void CSchemeManager::ReloadSchemes() +{ + int count = m_Schemes.Count(); + Shutdown( false ); + + // reload the scheme + for (int i = 1; i < count; i++) + { + LoadSchemeFromFile(m_Schemes[i]->GetFileName(), m_Schemes[i]->GetName()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: kills all the schemes +//----------------------------------------------------------------------------- +void CScheme::Shutdown( bool full ) +{ + for (int i = 0; i < m_BorderList.Count(); i++) + { + // delete if it's not shared + if (!m_BorderList[i].bSharedBorder) + { + IBorder *border = m_BorderList[i].border; + delete border; + } + } + + m_pBaseBorder = NULL; + m_BorderList.RemoveAll(); + m_pkvBorders = NULL; + + if (full && m_pData) + { + m_pData->deleteThis(); + m_pData = NULL; + delete this; + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns a handle to the default (first loaded) scheme +//----------------------------------------------------------------------------- +HScheme CSchemeManager::GetDefaultScheme() +{ + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a handle to the scheme identified by "tag" +//----------------------------------------------------------------------------- +HScheme CSchemeManager::GetScheme(const char *tag) +{ + for (int i=1;i<m_Schemes.Count();i++) + { + if ( !stricmp(tag,m_Schemes[i]->GetName()) ) + { + return i; + } + } + return 1; // default scheme +} + +int CSchemeManager::GetProportionalScaledValue_( int rootWide, int rootTall, int normalizedValue ) +{ + int proH, proW; + g_pSurface->GetProportionalBase( proW, proH ); + double scale = (double)rootTall / (double)proH; + + return (int)( normalizedValue * scale ); +} + +int CSchemeManager::GetProportionalNormalizedValue_( int rootWide, int rootTall, int scaledValue ) +{ + int proH, proW; + g_pSurface->GetProportionalBase( proW, proH ); + float scale = (float)rootTall / (float)proH; + + return (int)( scaledValue / scale ); +} + +//----------------------------------------------------------------------------- +// Purpose: converts a value into proportional mode +//----------------------------------------------------------------------------- +int CSchemeManager::GetProportionalScaledValue(int normalizedValue) +{ + int wide, tall; + g_pSurface->GetScreenSize( wide, tall ); + return GetProportionalScaledValue_( wide, tall, normalizedValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: converts a value out of proportional mode +//----------------------------------------------------------------------------- +int CSchemeManager::GetProportionalNormalizedValue(int scaledValue) +{ + int wide, tall; + g_pSurface->GetScreenSize( wide, tall ); + return GetProportionalNormalizedValue_( wide, tall, scaledValue ); +} + +// gets the proportional coordinates for doing screen-size independant panel layouts +// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling) +int CSchemeManager::GetProportionalScaledValueEx( CScheme *pScheme, int normalizedValue ) +{ + VPANEL sizing = pScheme->GetSizingPanel(); + if ( !sizing ) + { + return GetProportionalScaledValue( normalizedValue ); + } + + int w, h; + g_pIPanel->GetSize( sizing, w, h ); + return GetProportionalScaledValue_( w, h, normalizedValue ); +} + +int CSchemeManager::GetProportionalNormalizedValueEx( CScheme *pScheme, int scaledValue ) +{ + VPANEL sizing = pScheme->GetSizingPanel(); + if ( !sizing ) + { + return GetProportionalNormalizedValue( scaledValue ); + } + + int w, h; + g_pIPanel->GetSize( sizing, w, h ); + return GetProportionalNormalizedValue_( w, h, scaledValue ); +} + +int CSchemeManager::GetProportionalScaledValueEx( HScheme scheme, int normalizedValue ) +{ + IScheme *pscheme = GetIScheme( scheme ); + if ( !pscheme ) + { + Assert( 0 ); + return GetProportionalScaledValue( normalizedValue ); + } + + CScheme *p = static_cast< CScheme * >( pscheme ); + return GetProportionalScaledValueEx( p, normalizedValue ); +} + +int CSchemeManager::GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue ) +{ + IScheme *pscheme = GetIScheme( scheme ); + if ( !pscheme ) + { + Assert( 0 ); + return GetProportionalNormalizedValue( scaledValue ); + } + + CScheme *p = static_cast< CScheme * >( pscheme ); + return GetProportionalNormalizedValueEx( p, scaledValue ); +} + +void CSchemeManager::SpewFonts( void ) +{ + for ( int i = 1; i < m_Schemes.Count(); i++ ) + { + m_Schemes[i]->SpewFonts(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CScheme::GetResourceString(const char *stringName) +{ + return m_pkvBaseSettings->GetString(stringName); +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to an image +//----------------------------------------------------------------------------- +IImage *CSchemeManager::GetImage(const char *imageName, bool hardwareFiltered) +{ + if ( !imageName || strlen(imageName) <= 0 ) // frame icons and the like are in the scheme file and may not be defined, so if this is null then fail silently + { + return NULL; + } + + // set up to search for the bitmap + CachedBitmapHandle_t searchBitmap; + searchBitmap.pBitmap = NULL; + + // Prepend 'vgui/'. Resource files try to load images assuming they live in the vgui directory. + // Used to do this in Bitmap::Bitmap, moved so that the s_pszSearchString is searching for the + // filename with 'vgui/' already added. + char szFileName[MAX_PATH]; + + //if ( Q_IsAbsolutePath(imageName) ) + //{ + // Q_strncpy( szFileName, imageName, sizeof(szFileName) ); + //} + //else + { + if ( Q_stristr( imageName, ".pic" ) ) + { + Q_snprintf( szFileName, sizeof(szFileName), "%s", imageName ); + } + else + { + Q_snprintf( szFileName, sizeof(szFileName), "vgui/%s", imageName ); + } + } + + s_pszSearchString = szFileName; + int i = m_Bitmaps.Find( searchBitmap ); + if (m_Bitmaps.IsValidIndex( i ) ) + { + return m_Bitmaps[i].pBitmap; + } + + // couldn't find the image, try and load it + CachedBitmapHandle_t hBitmap = { new Bitmap( szFileName, hardwareFiltered ) }; + m_Bitmaps.Insert( hBitmap ); + return hBitmap.pBitmap; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HTexture CSchemeManager::GetImageID(const char *imageName, bool hardwareFiltered) +{ + IImage *img = GetImage(imageName, hardwareFiltered); + return ((Bitmap *)img)->GetID(); +} + +//----------------------------------------------------------------------------- +// Delete a managed image +//----------------------------------------------------------------------------- +bool CSchemeManager::DeleteImage( const char *pImageName ) +{ + if ( !pImageName ) + { + // nothing to do + return false; + } + + // set up to search for the bitmap + CachedBitmapHandle_t searchBitmap; + searchBitmap.pBitmap = NULL; + + // Prepend 'vgui/'. Resource files try to load images assuming they live in the vgui directory. + // Used to do this in Bitmap::Bitmap, moved so that the s_pszSearchString is searching for the + // filename with 'vgui/' already added. + char szFileName[256]; + if ( Q_stristr( pImageName, ".pic" ) ) + { + Q_snprintf( szFileName, sizeof(szFileName), "%s", pImageName ); + } + else + { + Q_snprintf( szFileName, sizeof(szFileName), "vgui/%s", pImageName ); + } + s_pszSearchString = szFileName; + + int i = m_Bitmaps.Find( searchBitmap ); + if ( !m_Bitmaps.IsValidIndex( i ) ) + { + // not found + return false; + } + + // no way to know if eviction occured, assume it does + m_Bitmaps[i].pBitmap->Evict(); + delete m_Bitmaps[i].pBitmap; + m_Bitmaps.RemoveAt( i ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to an existing border +//----------------------------------------------------------------------------- +IBorder *CScheme::GetBorder(const char *borderName) +{ + int symbol = KeyValuesSystem()->GetSymbolForString(borderName); + for (int i = 0; i < m_BorderList.Count(); i++) + { + if (m_BorderList[i].borderSymbol == symbol) + { + return m_BorderList[i].border; + } + } + + return m_pBaseBorder; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the number of borders +//----------------------------------------------------------------------------- +int CScheme::GetBorderCount() const +{ + return m_BorderList.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the border at the given index +//----------------------------------------------------------------------------- +IBorder *CScheme::GetBorderAtIndex( int iIndex ) +{ + if ( !m_BorderList.IsValidIndex( iIndex ) ) + return NULL; + + return m_BorderList[ iIndex ].border; +} + +//----------------------------------------------------------------------------- +// Finds a font in the alias list +//----------------------------------------------------------------------------- +HFont CScheme::FindFontInAliasList( const char *fontName ) +{ + int i = m_FontAliases.Find( fontName ); + if ( i != m_FontAliases.InvalidIndex() ) + { + return m_FontAliases[i]._font; + } + + // No dice + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : font - +// Output : char const +//----------------------------------------------------------------------------- +char const *CScheme::GetFontName( const HFont& font ) +{ + for (int i = m_FontAliases.Count(); --i >= 0; ) + { + HFont fnt = (HFont)m_FontAliases[i]._font; + if ( fnt == font ) + return m_FontAliases[i]._trueFontName.String(); + } + + return "<Unknown font>"; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to an existing font, proportional=false means use default +//----------------------------------------------------------------------------- +HFont CScheme::GetFont( const char *fontName, bool proportional ) +{ + // First look in the list of aliases... + return FindFontInAliasList( GetMungedFontName( fontName, tag, proportional ) ); +} + +//----------------------------------------------------------------------------- +// Purpose:Get the number of fonts +//----------------------------------------------------------------------------- +int CScheme::GetFontCount() const +{ + return m_FontAliases.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the font at the given index +//----------------------------------------------------------------------------- +HFont CScheme::GetFontAtIndex( int iIndex ) +{ + if ( !m_FontAliases.IsValidIndex( iIndex ) ) + return INVALID_FONT; + + return m_FontAliases[ iIndex ]._font; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a char string of the munged name this font is stored as in the font manager +//----------------------------------------------------------------------------- +const char *CScheme::GetMungedFontName( const char *fontName, const char *scheme, bool proportional ) +{ + static char mungeBuffer[ 64 ]; + if ( scheme ) + { + Q_snprintf( mungeBuffer, sizeof( mungeBuffer ), "%s%s-%s", fontName, scheme, proportional ? "p" : "no" ); + } + else + { + Q_snprintf( mungeBuffer, sizeof( mungeBuffer ), "%s-%s", fontName, proportional ? "p" : "no" ); // we don't want the "(null)" snprintf appends + } + return mungeBuffer; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets a color from the scheme file +//----------------------------------------------------------------------------- +Color CScheme::GetColor(const char *colorName, Color defaultColor) +{ + const char *pchT = LookupSchemeSetting(colorName); + if (!pchT) + return defaultColor; + + int r = 0, g = 0, b = 0, a = 0; + if (sscanf(pchT, "%d %d %d %d", &r, &g, &b, &a) >= 3) + return Color(r, g, b, a); + + return defaultColor; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the color at the given index +//----------------------------------------------------------------------------- +const KeyValues *CScheme::GetColorData() const +{ + return m_pkvColors; +} + +//----------------------------------------------------------------------------- +// Purpose: recursively looks up a setting +//----------------------------------------------------------------------------- +const char *CScheme::LookupSchemeSetting(const char *pchSetting) +{ + // try parse out the color + int r, g, b, a = 0; + int res = sscanf(pchSetting, "%d %d %d %d", &r, &g, &b, &a); + if (res >= 3) + { + return pchSetting; + } + + // check the color area first + const char *colStr = m_pkvColors->GetString(pchSetting, NULL); + if (colStr) + return colStr; + + // check base settings + colStr = m_pkvBaseSettings->GetString(pchSetting, NULL); + if (colStr) + { + return LookupSchemeSetting(colStr); + } + + return pchSetting; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the minimum font height for the current language +//----------------------------------------------------------------------------- +int CScheme::GetMinimumFontHeightForCurrentLanguage() +{ + char language[64]; + bool bValid; + if ( IsPC() ) + { + bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof(language)-1 ); + } + else + { + Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) ); + bValid = true; + } + + if ( bValid ) + { + if (!stricmp(language, "korean") + || !stricmp(language, "tchinese") + || !stricmp(language, "schinese") + || !stricmp(language, "japanese")) + { + // the bitmap-based fonts for these languages simply don't work with a pt. size of less than 9 (13 pixels) + return 13; + } + + if ( !stricmp(language, "thai" ) ) + { + // thai has problems below 18 pts + return 18; + } + } + + // no special minimum height required + return 0; +} diff --git a/vgui2/src/Surface.cpp b/vgui2/src/Surface.cpp new file mode 100644 index 0000000..2f62320 --- /dev/null +++ b/vgui2/src/Surface.cpp @@ -0,0 +1,4082 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#define WIN32_LEAN_AND_MEAN +#define OEMRESOURCE + +// SRC only +#define PROTECTED_THINGS_DISABLE + +#include <windows.h> +#include <imm.h> +#include <zmouse.h> +#include <shellapi.h> +#pragma warning( disable : 4201 ) +#include <mmsystem.h> +#pragma warning( default : 4201 ) +#include <oleidl.h> +#include <stdio.h> +#include <basetypes.h> + +#include <vgui/VGUI.h> +#include <vgui/Dar.h> +#include <vgui/IClientPanel.h> +#include <vgui/ISurface.h> +#include <vgui/IInputInternal.h> +#include <vgui/IPanel.h> +#include <vgui/ISystem.h> +#include <vgui/ILocalize.h> +#include <vgui/IHTML.h> +#include <vgui/IVGui.h> +#include <vgui/IPanel.h> +#include <vgui/IScheme.h> + +#include <vgui/Cursor.h> +#include <vgui/KeyCode.h> +#include <KeyValues.h> +#include <vgui/MouseCode.h> + +#include "vgui_internal.h" +#include "bitmap.h" +#include "VPanel.h" + +#include "utlvector.h" +#include "utlsymbol.h" +#include "tier1/utldict.h" + +#include "filesystem.h" +#include "SteamBootStrapper.h" + +#include "vgui_surfacelib/Win32Font.h" +#include "vgui_surfacelib/FontManager.h" +#include "vgui_key_translation.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef PlaySound +#undef PlaySound +#endif + +#ifdef CreateFont +#undef CreateFont +#endif + +using namespace vgui; + +#define PLAT(vpanel) (((VPanel *)vpanel)->Plat()) + +namespace vgui +{ +class SurfacePlat +{ +public: + HWND hwnd; + HDC hdc; + HDC hwndDC; + HDC textureDC; + HGLRC hglrc; + HRGN clipRgn; + HBITMAP bitmap; + int bitmapSize[2]; + int restoreInfo[4]; + bool isFullscreen; + bool disabled; // whether the window can take user input + int fullscreenInfo[3]; + VPanel *embeddedPanel; + + bool isToolbar; // whether it has an icon in the tool tray or not + HICON notifyIcon; + VPANEL notifyPanel; + + HPanel lastKeyFocusIndex; // index to the last panel to have the focus +}; + +class Texture +{ +public: + int _id; + HBITMAP _bitmap; + HBITMAP _maskBitmap; + bool _bMask; + HICON _icon; + int _wide; + int _tall; + void *_dib; + void *_maskDib; + const char *_filename; +}; +} + +//----------------------------------------------------------------------------- +// Purpose: Implementation of ISurface for use running under windows, renders using GDI +// This is not used in the game, EngineSurface is used instead +//----------------------------------------------------------------------------- +class CWin32Surface : public ISurface +{ +public: + friend class CIconImage; + + CWin32Surface(); + ~CWin32Surface(); + virtual void Shutdown(); + virtual void RunFrame(); + virtual VPANEL GetEmbeddedPanel(); + virtual void SetEmbeddedPanel( VPANEL panel); + + // initializes the surface with the current window state + virtual void SetCurrentContextPanel(VPANEL panel); + virtual void PushMakeCurrent(VPANEL panel,bool useInsets); + virtual void PopMakeCurrent(VPANEL panel); + + virtual void DrawSetColor(int r, int g, int b, int a); + virtual void DrawSetColor(Color col); + virtual void DrawFilledRect(int x0, int y0, int x1, int y1); + virtual void DrawFilledRectFastFade( int x0, int y0, int x1, int y1, int fadeStartPt, int fadeEndPt, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ); + virtual void DrawFilledRectFade( int x0, int y0, int x1, int y1, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ); + virtual void DrawFilledRectArray( IntRect *pRects, int numRects ); + virtual void DrawOutlinedRect(int x0, int y0, int x1, int y1); + virtual void DrawLine(int x0, int y0, int x1, int y1); + virtual void DrawPolyLine(int *px, int *py, int numPoints); + virtual void DrawSetTextFont(HFont font); + virtual void DrawSetTextColor(int r, int g, int b, int a); + virtual void DrawSetTextColor(Color col); + virtual void DrawSetTextPos(int x, int y); + virtual void DrawSetTextScale(float sx, float sy); + virtual void DrawPrintText(const wchar_t *, int textLen, FontDrawType_t drawType = FONT_DRAW_DEFAULT); + virtual void DrawUnicodeChar(wchar_t wch, FontDrawType_t drawType = FONT_DRAW_DEFAULT ); + virtual void DrawUnicodeString( const wchar_t *pwString, FontDrawType_t drawType = FONT_DRAW_DEFAULT ); + virtual void DrawGetTextPos(int& x,int& y); + + virtual bool DrawGetTextureFile(int id, char *filename, int maxlen ); + virtual int DrawGetTextureId( char const *filename ); + virtual void DrawSetTextureFile(int id, const char *filename, int hardwareFilter, bool forceReload = false); + virtual void DrawSetTexture(int id); + virtual void DrawSetTextureRGBA(int id, const unsigned char *rgba, int wide, int tall, int hardwareFilter, bool forceReload = false); + virtual void DrawSetTextureRGBAEx(int id, const unsigned char *rgba, int wide, int tall, ImageFormat imageFormat ); + virtual void DrawGetTextureSize(int id, int &wide, int &tall); + virtual IVguiMatInfo *DrawGetTextureMatInfoFactory( int id ) { return NULL; } + virtual void DrawTexturedRect(int x0, int y0, int x1, int y1); + virtual int CreateNewTextureID( bool procedural ); + virtual bool IsTextureIDValid(int id); + virtual void FreeTextureData( Texture *pTexture ); + virtual bool DeleteTextureByID(int id); + virtual void DrawFlushText(); + virtual IHTML *CreateHTMLWindow(vgui::IHTMLEvents *events, VPANEL context); + virtual void PaintHTMLWindow(IHTML *htmlwin); + virtual void DeleteHTMLWindow(IHTML *htmlwin); + + virtual VPANEL GetNotifyPanel(); + virtual void SetNotifyIcon(VPANEL context, HTexture icon, VPANEL panelToReceiveMessages, const char *text); + virtual void SetPanelForInput( VPANEL vpanel ); + + virtual void GetScreenSize(int &wide, int &tall); + + virtual void SetAsTopMost(VPANEL panel, bool state); + virtual void BringToFront(VPANEL panel); + virtual void SetForegroundWindow(VPANEL panel); + virtual void SetPanelVisible(VPANEL panel, bool visible); + virtual void SetMinimized(VPANEL panel, bool state); + virtual bool IsMinimized(VPANEL panel); + virtual void FlashWindow(VPANEL panel, bool state); + virtual void SetTitle(VPANEL panel, const wchar_t *title); + virtual void SetAsToolBar(VPANEL panel, bool state); + virtual bool SupportsFeature(SurfaceFeature_e feature); + virtual void SetTopLevelFocus(VPANEL panel); + + virtual int GetPopupCount(); + virtual VPANEL GetPopup(int index); + virtual void AddPanel(VPANEL panel); + virtual void ReleasePanel(VPANEL panel); + virtual void CreatePopup(VPANEL panel, bool minimised, bool showTaskbarIcon, bool disabled, bool mouseInput, bool kbInput ); + virtual bool RecreateContext(VPANEL panel); + virtual void EnableMouseCapture(VPANEL panel, bool state); + virtual bool ShouldPaintChildPanel(VPANEL childPanel); + virtual void MovePopupToFront(VPANEL panel); + virtual void MovePopupToBack(VPANEL panel); + virtual void SwapBuffers(VPANEL panel); + virtual void Invalidate(VPANEL panel); + virtual void SetCursor(HCursor cursor); + virtual void SetCursorAlwaysVisible( bool visible ) {} + virtual void ApplyChanges(); + virtual bool IsWithin(int x, int y); + virtual bool HasFocus(); + virtual void GetWorkspaceBounds(int &x, int &y, int &wide, int &tall); + virtual void SolveTraverse(VPANEL panel, bool forceApplySchemeSettings); + virtual void PaintTraverse(VPANEL panel); + + + virtual void RestrictPaintToSinglePanel(VPANEL panel); + virtual void SetModalPanel(VPANEL ); + virtual VPANEL GetModalPanel(); + virtual void UnlockCursor(); + virtual void LockCursor(); + virtual void SetTranslateExtendedKeys(bool state); + virtual VPANEL GetTopmostPopup(); + + + // sound + virtual void PlaySound(const char *fileName); + + // fonts + virtual HFont CreateFont(); + virtual bool SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin = 0, int nRangeMax = 0); + virtual int GetFontTall(HFont font); + virtual int GetFontTallRequested(HFont font); + virtual int GetFontAscent(HFont font, wchar_t wch); + virtual void GetCharABCwide(HFont font, int ch, int &a, int &b, int &c); + virtual int GetCharacterWidth(HFont font, int ch); + virtual void GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall); + virtual bool AddCustomFontFile(const char *fontName, const char *fontFileName); + virtual bool AddBitmapFontFile(const char *fontFileName); + virtual void SetBitmapFontName( const char *pName, const char *pFontFilename ); + virtual const char *GetBitmapFontName( const char *pName ); + virtual bool SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags); + virtual bool IsFontAdditive(HFont font); + virtual void PrecacheFontCharacters(HFont font, const wchar_t *pCharacters); + virtual void ClearTemporaryFontCache( void ); + virtual const char *GetFontName( HFont font ); + virtual const char *GetFontFamilyName( HFont font ); + + virtual bool IsCursorVisible() { return true; } + + // gets the absolute coordinates of the screen (in screen space) + void GetAbsoluteWindowBounds(int &x, int &y, int &wide, int &tall); + + // methods + void setFocus(VPANEL panel); + + void GetProportionalBase( int &width, int &height ) { width = BASE_WIDTH; height = BASE_HEIGHT; } + + virtual void CalculateMouseVisible(); + virtual bool NeedKBInput(); + + // we use the default IInput cursor functions + virtual bool HasCursorPosFunctions() { return false; } + virtual void SurfaceGetCursorPos(int &x, int &y) {} + virtual void SurfaceSetCursorPos(int x, int y){} + + virtual void SetAllowHTMLJavaScript( bool state ); + // SRC specific interfaces + virtual void DrawTexturedLine( const Vertex_t &a, const Vertex_t &b ); + virtual void DrawOutlinedCircle(int x, int y, int radius, int segments) ; + virtual void DrawTexturedPolyLine( const Vertex_t *p,int n ) ; // (Note: this connects the first and last points). + virtual void DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ); + virtual void DrawTexturedPolygon(int n, Vertex_t *pVertices, bool bClipVertices = true); + virtual const wchar_t *GetTitle(VPANEL panel); + virtual void LockCursor( bool state ); + virtual bool IsCursorLocked( void ) const; + virtual void SetWorkspaceInsets( int left, int top, int right, int bottom ); + // Lower level char drawing code, call DrawGet then pass in info to DrawRender (NOT SUPPORTED BY DEFAULT SURFACE )!!! + virtual bool DrawGetUnicodeCharRenderInfo( wchar_t ch, CharRenderInfo& info ); + virtual void DrawRenderCharFromInfo( const CharRenderInfo& info ); + + // alpha multipliers not yet implemented + virtual void DrawSetAlphaMultiplier( float alpha /* [0..1] */ ) {} + virtual float DrawGetAlphaMultiplier() { return 1.0f; } + + // Here's where the app systems get to learn about each other + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + + // Here's where systems can access other interfaces implemented by this object + // Returns NULL if it doesn't implement the requested interface + virtual void *QueryInterface( const char *pInterfaceName ); + + // Init, shutdown + virtual InitReturnVal_t Init(); + //virtual void Shutdown(); + + // screen size changing + void OnScreenSizeChanged( int nOldWidth, int nOldHeight ) + { + } + + // We don't support this for non material system surfaces (we could) + virtual vgui::HCursor CreateCursorFromFile( char const *curOrAniFile, char const *pPathID ) + { + Assert( 0 ); + return dc_arrow; + } + + virtual void PaintTraverseEx(VPANEL panel, bool paintPopups = false ) + { + PaintTraverse( panel ); + } + + virtual float GetZPos() const + { + return 0.0f; + } + + virtual IImage *GetIconImageForFullPath( char const *pFullPath ); + + virtual const char *GetResolutionKey( void ) const + { + Assert( 0 ); + return NULL; + } + + virtual bool ForceScreenSizeOverride( bool bState, int wide, int tall ); + // LocalToScreen, ParentLocalToScreen fixups for explicit PaintTraverse calls on Panels not at 0, 0 position + virtual bool ForceScreenPosOffset( bool bState, int x, int y ); + + virtual void OffsetAbsPos( int &x, int &y ); + + virtual bool IsScreenSizeOverrideActive( void ); + virtual bool IsScreenPosOverrideActive( void ); + + void GetKernedCharWidth( HFont font, wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &flabcA ) + { + Assert( 0 ); + wide = 0.0f; + flabcA = 0.0f; + } + + // split screen state changed, etc. + void ResetFontCaches() + { + } + + virtual void DestroyTextureID( int id ); + + virtual int GetTextureNumFrames( int id ); + virtual void DrawSetTextureFrame( int id, int nFrame, unsigned int *pFrameCache ); + + + virtual void DrawUpdateRegionTextureRGBA( int nTextureID, int x, int y, const unsigned char *pchData, int wide, int tall, ImageFormat imageFormat ) + { + } + virtual bool BHTMLWindowNeedsPaint(IHTML *htmlwin) + { + return false; + } + + + virtual const char *GetWebkitHTMLUserAgentString(); + + virtual void *Deprecated_AccessChromeHTMLController() { return NULL; } + + virtual void SetFullscreenViewport( int x, int y, int w, int h ) OVERRIDE + { + m_nFullscreenViewportX = x; + m_nFullscreenViewportY = y; + m_nFullscreenViewportWidth = w; + m_nFullscreenViewportHeight = h; + m_pFullscreenRenderTarget = NULL; + } + + virtual void GetFullscreenViewport( int & x, int & y, int & w, int & h ) OVERRIDE + { + x = m_nFullscreenViewportX; + y = m_nFullscreenViewportY; + w = m_nFullscreenViewportWidth; + h = m_nFullscreenViewportHeight; + } + virtual void PushFullscreenViewport(); + virtual void PopFullscreenViewport(); + + // software cursors aren't available in tools + virtual void SetSoftwareCursor( bool bUseSoftwareCursor ) OVERRIDE {} + virtual void PaintSoftwareCursor() OVERRIDE {} + +private: + + CUtlDict< IImage *, unsigned short > m_FileTypeImages; + + enum { BASE_HEIGHT = 480, BASE_WIDTH = 640 }; + + bool LoadTGA(Texture *texture, const char *filename); + bool LoadBMP(Texture *texture, const char *filename); + + void InternalThinkTraverse(VPANEL panel); + void InternalSolveTraverse(VPANEL panel); + void InternalSchemeSettingsTraverse(VPANEL panel, bool forceApplySchemeSettings); + + VPANEL GetContextPanelForChildPanel(VPANEL childPanel); + void initStaticData(); + + // sets the current line drawing color, called by DrawSetTextColor + void SetLineColor(Color col); + + VPANEL _embeddedPanel; // main panel + VPANEL _notifyPanel; + VPANEL _currentContextPanel; + HTexture _notifyIcon; + Dar<VPANEL> _popupList; // list of panels that have their own win32 window + HICON _currentCursor; + + OSVERSIONINFO m_WindowsVersion; + bool m_bSupportsUnicode; + CUtlVector<CUtlSymbol> m_CustomFontFileNames; + + // current font info + HFont m_hCurrentFont; + CWin32Font *m_pActiveFont; + HPEN pen; // the pen used to draw lines + + bool _needKB; + bool _needMouse; + + int m_TextPos[2]; + + Texture *m_pCurrentTexture; + static bool TextureLessFunc(const Texture &lhs, const Texture &rhs); + Texture *GetTextureById(int id); + Texture *AllocTextureForId(int id); + int GetNumTextures(); + + CUtlRBTree<Texture, int> m_VGuiSurfaceTextures; + + struct ScreenOverride_t + { + ScreenOverride_t() : m_bActive( false ) + { + m_nValue[ 0 ] = m_nValue[ 1 ] = 0; + } + bool m_bActive; + int m_nValue[ 2 ]; + }; + + ScreenOverride_t m_ScreenSizeOverride; + ScreenOverride_t m_ScreenPosOverride; + + bool m_bAllowJavaScript; + + int m_nFullscreenViewportX; + int m_nFullscreenViewportY; + int m_nFullscreenViewportWidth; + int m_nFullscreenViewportHeight; + ITexture *m_pFullscreenRenderTarget; + +}; + +CWin32Surface g_Surface; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CWin32Surface, ISurface, VGUI_SURFACE_INTERFACE_VERSION, g_Surface); + +//!! these defines duplicated in Surface_Win32.cpp +#define WM_MY_TRAY_NOTIFICATION (WM_USER+1) + +static UINT staticShutdownMsg = 0; +static HICON staticDefaultCursor[20]; +static WNDCLASS staticWndclass = { NULL }; +static ATOM staticWndclassAtom = 0; +static bool staticStaticDataInitialized = false; +static bool staticSurfaceAvailable; + +// these functions defined below +static LRESULT CALLBACK staticProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam); +static void staticNotifyIconProc(HWND hwnd, WPARAM wparam, LPARAM lparam); + +#ifdef DEBUG_TIMING + +#define START_TIMER() \ + float paintTime;\ + static LARGE_INTEGER ticksPerSecond = { 0 };\ + if (!ticksPerSecond.QuadPart)\ + {\ + QueryPerformanceFrequency(&ticksPerSecond);\ + }\ + LARGE_INTEGER Start, end;\ + QueryPerformanceCounter(&Start); + +#define END_TIMER(x) \ + QueryPerformanceCounter(&end);\ + paintTime = (float)(((end.QuadPart - Start.QuadPart) * 1000000) / ticksPerSecond.QuadPart) / 1000;\ + if (paintTime > 1.0f) Msg(x, paintTime); + + +#else +#define START_TIMER() +#define END_TIMER(x) + +#endif // DEBUG_TIMING + +//----------------------------------------------------------------------------- +// Purpose: Handles drag and drop +//----------------------------------------------------------------------------- +class CSurfaceDragDropTarget : public IDropTarget +{ +public: + CSurfaceDragDropTarget() + { + _refCount = 0; + _dragData = NULL; + OleInitialize(NULL); + } + +private: + unsigned int _refCount; + KeyValues *_dragData; + + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) + { + if (riid == IID_IDropTarget) + { + *ppvObject = (IDropTarget *)this; + return S_OK; + } + + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef( void) + { + return ++_refCount; + } + + virtual ULONG STDMETHODCALLTYPE Release( void) + { + return --_refCount; + } + + virtual HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) + { + if (_dragData) + { + _dragData->deleteThis(); + } + _dragData = calculateData(pDataObject); + return DragOver(grfKeyState, pt, pdwEffect); + } + + virtual HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) + { + *pdwEffect = DROPEFFECT_NONE; + + if (!_dragData || !g_pIVgui->IsRunning()) + return S_OK; + + // get the panel the mouse is over + VPANEL mouseOver = g_pInput->GetMouseOver(); + if (mouseOver) + { + // check to see if the panel will accept this message + if (((VPanel *)mouseOver)->Client()->RequestInfo(_dragData)) + { + *pdwEffect = DROPEFFECT_COPY; + } + } + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DragLeave() + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) + { + *pdwEffect = DROPEFFECT_NONE; + + if (!_dragData || !g_pIVgui->IsRunning()) + return S_OK; + + + // get the panel the mouse is over + VPANEL target = (VPANEL)_dragData->GetPtr("AcceptPanel"); + if (target && _dragData) + { + // check to see if the panel will accept this message + g_pIVgui->PostMessage(target, _dragData, NULL); + _dragData = NULL; + } + + if (_dragData) + { + _dragData->deleteThis(); + } + _dragData = NULL; + + return S_OK; + } + + // internal methods + virtual KeyValues *calculateData(IDataObject *pDataObject) + { + KeyValues *dragData = NULL; + + // check on the type of data + FORMATETC format = + { + CF_TEXT, + NULL, + DVASPECT_CONTENT, + -1, + TYMED_HGLOBAL + }; + STGMEDIUM storage; + + if (pDataObject->GetData(&format, &storage) == S_OK) + { + // we got some data + if (storage.tymed == TYMED_HGLOBAL) + { + const char *buf = (const char *)GlobalLock(storage.hGlobal); + dragData = new KeyValues("DragDrop", "type", "text", "text", buf); + GlobalUnlock(storage.hGlobal); + } + + ReleaseStgMedium(&storage); + } + else + { + // try getting a file + format.cfFormat = CF_HDROP; + if (pDataObject->GetData(&format, &storage) == S_OK && storage.tymed == TYMED_HGLOBAL) + { + dragData = new KeyValues("DragDrop", "type", "files"); + KeyValues *fileList = dragData->FindKey("list", true); + + // parse out the file list + HDROP hdrop = (HDROP)GlobalLock(storage.hGlobal); + char namebuf[32], buf[512]; + int count = DragQueryFile(hdrop, 0xFFFFFFFF, buf, 511); + for (int i = 0; i < count; i++) + { + Q_snprintf(namebuf, sizeof( namebuf ), "%d", i); + DragQueryFile(hdrop, i, buf, 511); + fileList->SetString(namebuf, buf); + } + GlobalUnlock(storage.hGlobal); + } + } + + return dragData; + } +}; + +static CSurfaceDragDropTarget staticDragDropTarget; + +bool CWin32Surface::TextureLessFunc(const Texture &lhs, const Texture &rhs) +{ + return lhs._id < rhs._id; +} + +Texture *CWin32Surface::GetTextureById(int id) +{ + Texture findTex = { id }; + int index = m_VGuiSurfaceTextures.Find(findTex); + if (m_VGuiSurfaceTextures.IsValidIndex(index)) + { + return &m_VGuiSurfaceTextures[index]; + } + + return NULL; +} + +Texture *CWin32Surface::AllocTextureForId(int id) +{ + Texture newTex = { id }; + int index = m_VGuiSurfaceTextures.Insert(newTex); + return &m_VGuiSurfaceTextures[index]; +} + +int CWin32Surface::GetNumTextures() +{ + return m_VGuiSurfaceTextures.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void staticGenerateIconForTexture(Texture *texture, HDC hdc) +{ + // see if there is an iconic version of the texture file first + char buf[256]; + Q_snprintf(buf, sizeof( buf ), "%s.ico", texture->_filename); + texture->_icon = (HICON)::LoadImage(NULL, buf, IMAGE_ICON, 16, 16, LR_LOADFROMFILE | LR_DEFAULTSIZE); + if (texture->_icon) + return; + + if (texture->_wide > 32 || texture->_tall > 32) + return; + + // generate an icon from the tga + + // generate the AND plane based on the alpha + uchar planeAND[32 * 32 / 8]; + + /* !! disabled until verified + // + // from the alpha of the bitmap, generate a bitmask + // + uchar *ptr = (uchar *)texture->_dib + 3; // jump to alpha portion + uchar *ptrEnd = (uchar *)texture->_dib + (texture->_wide * texture->_tall * 4); + uchar *andPtr = planeAND; + while (ptr < ptrEnd) + { + // Start all empty + *andPtr = 0; + + // assume the number of pixel is a multiple of 8 + // if the alpha is high enough, then mask out the pixel + + // it's two bits per pixel, strangely enough + if (*ptr > 127) { *andPtr |= 0x03; } ptr += 4; + if (*ptr > 127) { *andPtr |= 0x0C; } ptr += 4; + if (*ptr > 127) { *andPtr |= 0x30; } ptr += 4; + if (*ptr > 127) { *andPtr |= 0xC0; } ptr += 4; + + andPtr++; + } + */ + + memset(planeAND, 0x00, sizeof(planeAND)); + HBITMAP mask = ::CreateBitmap(texture->_wide, texture->_tall, 1, 1, planeAND); + + // create the icon + ICONINFO iconInfo; + iconInfo.fIcon = TRUE; + iconInfo.xHotspot = 8; + iconInfo.yHotspot = 8; + iconInfo.hbmMask = mask; + iconInfo.hbmColor = texture->_bitmap; + + texture->_icon = ::CreateIconIndirect(&iconInfo); + + ::DeleteObject(mask); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, basic variable initialization +//----------------------------------------------------------------------------- +CWin32Surface::CWin32Surface() : m_VGuiSurfaceTextures(0, 128, TextureLessFunc) +{ + _currentCursor = NULL; + m_pCurrentTexture = NULL; + + initStaticData(); + + staticSurfaceAvailable = false; + m_bAllowJavaScript = false; + + m_hCurrentFont = 0; + + pen = NULL; + + _needKB = true; + _needMouse = true; + + HRESULT hr; + + // this step is IMPORTANT , it turns "on" COM + if (FAILED(hr = CoInitialize(NULL))) + { + // failed + } + + // get our version info + m_WindowsVersion.dwOSVersionInfoSize = sizeof(m_WindowsVersion); + GetVersionEx(&m_WindowsVersion); + if (m_WindowsVersion.dwMajorVersion >= 5) + { + m_bSupportsUnicode = true; + } + else + { + m_bSupportsUnicode = false; + } + + m_TextPos[0] = m_TextPos[1] = 0; + + m_nFullscreenViewportX = m_nFullscreenViewportY = 0; + m_nFullscreenViewportWidth = m_nFullscreenViewportHeight = 0; + m_pFullscreenRenderTarget = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CWin32Surface::~CWin32Surface() +{ + // ensure we don't try and process any more windows messages + staticSurfaceAvailable = false; + + // free all the textures + + for (int i = 0; i < m_VGuiSurfaceTextures.MaxElement(); i++) + { + if (!m_VGuiSurfaceTextures.IsValidIndex(i)) + continue; + + Texture *texture = &m_VGuiSurfaceTextures[i]; + + if (texture->_bitmap) + { + ::DeleteObject(texture->_bitmap); + } + + if (texture->_maskBitmap) + { + ::DeleteObject(texture->_maskBitmap); + } + + if (texture->_icon) + { + ::DestroyIcon(texture->_icon); + } + } + + CoUninitialize(); // turn com off +} + +//----------------------------------------------------------------------------- +// Purpose: Shuts down app +//----------------------------------------------------------------------------- +void CWin32Surface::Shutdown() +{ + for ( int i = m_FileTypeImages.First(); i != m_FileTypeImages.InvalidIndex(); i = m_FileTypeImages.Next( i ) ) + { + delete m_FileTypeImages[ i ]; + } + m_FileTypeImages.RemoveAll(); + + // free the fonts + FontManager().ClearAllFonts(); + + // release any custom font files + {for (int i = 0; i < m_CustomFontFileNames.Count(); i++) + { + ::RemoveFontResource(m_CustomFontFileNames[i].String()); + }} + m_CustomFontFileNames.RemoveAll(); + + // ensure we don't try and process windows messages during Shutdown + staticSurfaceAvailable = false; + + // release any panels still with surfaces + while (GetPopupCount()) + { + ReleasePanel(GetPopup(0)); + } + + // kill our windows instance + ::UnregisterClass("Surface", ::GetModuleHandle(NULL)); + staticWndclassAtom = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CWin32Surface::GetEmbeddedPanel() +{ + return _embeddedPanel; +} + + // SRC specific interfaces + void CWin32Surface::DrawTexturedLine( const Vertex_t &a, const Vertex_t &b ) + { + + } + void CWin32Surface::DrawOutlinedCircle(int x, int y, int radius, int segments) + { + + } + void CWin32Surface::DrawTexturedPolyLine( const Vertex_t *p,int n ) + { + } + void CWin32Surface::DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ) + { + } + void CWin32Surface::DrawTexturedPolygon(int n, Vertex_t *pVertices, bool bClipVertices /*= true*/) + { + NOTE_UNUSED( bClipVertices ); + + POINT *pt; + HDC hdc = PLAT(_currentContextPanel)->hdc; + + pt = (POINT *)malloc(sizeof(POINT) * n); + if(pt) + { + for(int i=0;i<n;i++) + { + pt[i].x= pVertices[i].m_Position.x; + pt[i].y= pVertices[i].m_Position.y; + } + + COLORREF pencolor = ::GetTextColor(hdc); + COLORREF brushcolor = ::GetBkColor(hdc); + + //Set the pen color to the current brush color to avoid strange outlines on our polygons + DrawSetTextColor(GetRValue(brushcolor),GetGValue(brushcolor),GetBValue(brushcolor),255); + + //create a brush + HBRUSH hbrush = ::CreateSolidBrush(brushcolor); + HBRUSH oldBrush = (HBRUSH)::SelectObject(hdc, hbrush); + + ::Polygon(hdc, pt, n); + + ::SelectObject(hdc, oldBrush); + ::DeleteObject(hbrush); + free(pt); + + //restore pen colour + DrawSetTextColor(GetRValue(pencolor),GetGValue(pencolor),GetBValue(pencolor),255); + } + } + + const wchar_t *CWin32Surface::GetTitle(VPANEL panel) + { + return L""; + } + void CWin32Surface::LockCursor( bool state ) + { + } + bool CWin32Surface::IsCursorLocked( void ) const + { + return false; + } + void CWin32Surface::SetWorkspaceInsets( int left, int top, int right, int bottom ) + { + } + //----------------------------------------------------------------------------- +// Connect, disconnect... +//----------------------------------------------------------------------------- +bool CWin32Surface::Connect( CreateInterfaceFn factory ) +{ +return true; +} + +void CWin32Surface::Disconnect() +{ +} + + +//----------------------------------------------------------------------------- +// Access to other interfaces... +//----------------------------------------------------------------------------- +void *CWin32Surface::QueryInterface( const char *pInterfaceName ) +{ + return NULL; +} + + +//----------------------------------------------------------------------------- +// Initialization and shutdown... +//----------------------------------------------------------------------------- +InitReturnVal_t CWin32Surface::Init( void ) +{ + return INIT_FAILED; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Sets up the panel for use +// Input : *embeddedPanel - Main panel that becomes the top of the hierarchy +//----------------------------------------------------------------------------- +void CWin32Surface::SetEmbeddedPanel( VPANEL panel ) +{ + _embeddedPanel = panel; + + staticSurfaceAvailable = true; + + SetCurrentContextPanel(panel); + CreatePopup(panel, false, true, false, true, true); + + // send a message to ourselves every 50ms (20Hz) so that we don't block in the message queue too long + ::SetTimer(PLAT(_currentContextPanel)->hwnd, 0, 50, (TIMERPROC) NULL); + + // fonts initialization + char language[64]; + if (g_pSystem->GetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof(language)-1)) + { + FontManager().SetLanguage(language); + } + else + { + FontManager().SetLanguage("english"); + } +} + +// Lower level char drawing code, call DrawGet then pass in info to DrawRender +bool CWin32Surface::DrawGetUnicodeCharRenderInfo( wchar_t ch, CharRenderInfo& info ) +{ + // Only supported in engine renderer! + Assert( 0 ); + info.valid = false; + return false; +} + +void CWin32Surface::DrawRenderCharFromInfo( const CharRenderInfo& info ) +{ + Assert( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the current drawing context +//----------------------------------------------------------------------------- +void CWin32Surface::SetCurrentContextPanel(VPANEL panel) +{ + _currentContextPanel = panel; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CWin32Surface::GetContextPanelForChildPanel(VPANEL childPanel) +{ + VPANEL contextPanel = childPanel; + while (contextPanel && !PLAT(contextPanel)) + { + contextPanel = (VPANEL)((VPanel *)contextPanel)->GetParent(); + } + return contextPanel; +} + +// static currently used win32 Paint info +static ::PAINTSTRUCT s_CurrentPaintStruct; + +void CWin32Surface::PushMakeCurrent(VPANEL panel, bool useInsets) +{ + // find the win32 window context from the hierarchy + VPANEL currentContextPanel = GetContextPanelForChildPanel(panel); + + SetCurrentContextPanel(currentContextPanel); + + // clear the current active font so that it will be reset + m_pActiveFont = NULL; + + if (!currentContextPanel) + { + // no drawing context found, something is seriously wrong + Msg( "Warning: VPanel with no drawing context\n" ); + } + + int inset[4]; + + //!! need to make the inset part of VPanel + ((VPanel *)panel)->Client()->GetInset(inset[0],inset[1],inset[2],inset[3]); + + if(!useInsets) + { + inset[0]=0; + inset[1]=0; + inset[2]=0; + inset[3]=0; + } + + int absThis[4]; + ((VPanel *)_currentContextPanel)->GetAbsPos(absThis[0], absThis[1]); + ((VPanel *)_currentContextPanel)->GetSize(absThis[2], absThis[3]); + absThis[2] += absThis[0]; + absThis[3] += absThis[1]; + + int absPanel[4]; + ((VPanel *)panel)->GetAbsPos(absPanel[0], absPanel[1]); + ((VPanel *)panel)->GetSize(absPanel[2], absPanel[3]); + absPanel[2] += absPanel[0]; + absPanel[3] += absPanel[1]; + + int clipRect[4]; + ((VPanel *)panel)->Client()->GetClipRect(clipRect[0],clipRect[1],clipRect[2],clipRect[3]); + + if ( _currentContextPanel == panel ) + { + // this panel has it's own window, so use screen space + ::SetViewportOrgEx(PLAT(_currentContextPanel)->hdc,0+inset[0],0+inset[1],null); + } + else + { + // child window, so set win32 up so all subsequent drawing calls are done in local space + ::SetViewportOrgEx(PLAT(_currentContextPanel)->hdc,(absPanel[0]+inset[0])-absThis[0],(absPanel[1]+inset[1])-absThis[1],null); + } + + // setup clipping + // get and translate clipRect into surface space, then factor in inset + int x0 = clipRect[0] - absThis[0]; + int y0 = clipRect[1] - absThis[1]; + int x1 = (clipRect[2] - absThis[0]) - inset[2]; + int y1 = (clipRect[3] - absThis[1]) - inset[3]; + + //set the rect and select to make it current + ::SetRectRgn(PLAT(_currentContextPanel)->clipRgn,x0,y0,x1,y1); + ::SelectObject(PLAT(_currentContextPanel)->hdc, PLAT(_currentContextPanel)->clipRgn); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::PopMakeCurrent(VPANEL panel) +{ + if (panel == _currentContextPanel) + { + // reset the current panel to be the main panel + SetCurrentContextPanel(_embeddedPanel); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::GetScreenSize(int &wide, int &tall) +{ + if ( m_ScreenSizeOverride.m_bActive ) + { + wide = m_ScreenSizeOverride.m_nValue[ 0 ]; + tall = m_ScreenSizeOverride.m_nValue[ 1 ]; + return; + } + + VPANEL context = _embeddedPanel; + if (context) + { + wide = ::GetDeviceCaps(PLAT(context)->hdc, HORZRES); + tall = ::GetDeviceCaps(PLAT(context)->hdc, VERTRES); + } +} + +bool CWin32Surface::ForceScreenSizeOverride( bool bState, int wide, int tall ) +{ + bool bWasSet = m_ScreenSizeOverride.m_bActive; + m_ScreenSizeOverride.m_bActive = bState; + m_ScreenSizeOverride.m_nValue[ 0 ] = wide; + m_ScreenSizeOverride.m_nValue[ 1 ] = tall; + return bWasSet; +} + +// LocalToScreen, ParentLocalToScreen fixups for explicit PaintTraverse calls on Panels not at 0, 0 position +bool CWin32Surface::ForceScreenPosOffset( bool bState, int x, int y ) +{ + bool bWasSet = m_ScreenPosOverride.m_bActive; + m_ScreenPosOverride.m_bActive = bState; + m_ScreenPosOverride.m_nValue[ 0 ] = x; + m_ScreenPosOverride.m_nValue[ 1 ] = y; + return bWasSet; +} + +void CWin32Surface::OffsetAbsPos( int &x, int &y ) +{ + if ( !m_ScreenPosOverride.m_bActive ) + return; + + x += m_ScreenPosOverride.m_nValue[ 0 ]; + y += m_ScreenPosOverride.m_nValue[ 1 ]; +} + +bool CWin32Surface::IsScreenSizeOverrideActive( void ) +{ + return ( m_ScreenSizeOverride.m_bActive ); +} + +bool CWin32Surface::IsScreenPosOverrideActive( void ) +{ + return ( m_ScreenPosOverride.m_bActive ); +} + +void CWin32Surface::DestroyTextureID( int id ) +{ + // not implemented +} + +int CWin32Surface::GetTextureNumFrames( int id ) +{ + // not implemented + return 0; +} + +void CWin32Surface::DrawSetTextureFrame( int id, int nFrame, unsigned int *pFrameCache ) +{ + // not implemented +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CWin32Surface::GetNotifyPanel() +{ + return _notifyPanel; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::SetNotifyIcon(VPANEL context, HTexture iconID, VPANEL panelToReceiveMessages, const char *text) +{ + context = GetContextPanelForChildPanel(context); + if (!context) + return; + + if (!text) + { + text = ""; + } + + // things haven't changed so just update the tooltip + if (_notifyIcon == iconID && _notifyPanel == panelToReceiveMessages && context && PLAT(context)->notifyIcon) + { + ::NOTIFYICONDATA iconData = + { + sizeof(iconData), + PLAT(context)->hwnd, + 1, // icon ID + NIF_TIP, // modify tooltip only + WM_MY_TRAY_NOTIFICATION, // callback message + PLAT(context)->notifyIcon, // icon handle + "", // tooltip text + }; + + strncpy(iconData.szTip, text, 63); + iconData.szTip[63] = '\0'; + ::Shell_NotifyIcon(NIM_MODIFY, &iconData); + return; + } + + _notifyIcon = iconID; + _notifyPanel = panelToReceiveMessages; + + DWORD dwMessage = NIM_MODIFY; + if (!iconID) + { + dwMessage = NIM_DELETE; + PLAT(context)->notifyIcon = NULL; + } + else if (!PLAT(context)->notifyIcon) + { + dwMessage = NIM_ADD; + } + + // make sure the icon has been loaded + Texture *texture = NULL; + if (iconID) + { + texture = GetTextureById(iconID); + + if (!texture->_icon) + { + // generate an icon for the texture + staticGenerateIconForTexture(texture, PLAT(_currentContextPanel)->hdc); + } + } + + // get the icon + if (texture) + { + PLAT(context)->notifyIcon = texture->_icon; + } + + ::NOTIFYICONDATA iconData = + { + sizeof(iconData), + PLAT(context)->hwnd, + 1, // icon ID + NIF_ICON | NIF_TIP | NIF_MESSAGE, // items used + WM_MY_TRAY_NOTIFICATION, // callback message + PLAT(context)->notifyIcon, // icon handle + "", // tooltip text + }; + + strncpy(iconData.szTip, text, 63); + iconData.szTip[63] = '\0'; + + BOOL success = ::Shell_NotifyIcon(dwMessage, &iconData); + + if (iconID && !success) + { + DWORD err = GetLastError(); + Msg("error: SetNotifyIcon(%d) failed\n", err); + } +} + +void CWin32Surface::DrawSetColor(int r,int g,int b,int a) +{ + SetBkColor(PLAT(_currentContextPanel)->hdc,RGB(r,g,b)); +} + +void CWin32Surface::DrawSetColor(Color col) +{ + DrawSetColor(col[0], col[1], col[2], col[3]); +} + +void CWin32Surface::DrawSetTextPos(int x, int y) +{ + MoveToEx(PLAT(_currentContextPanel)->hdc,x,y,null); + m_TextPos[0] = x; + m_TextPos[1] = y; +} + +void CWin32Surface::DrawGetTextPos(int& x,int& y) +{ + x = m_TextPos[0]; + y = m_TextPos[1]; +} + + +void CWin32Surface::DrawSetTextFont(HFont font) +{ + Assert(font); + + // make the font current + m_hCurrentFont = font; + +// m_FontAmalgams[m_hCurrentFont].GetFontForChar('a')->SetAsActiveFont(_currentContextPanel->Plat()->hdc); +} + +void CWin32Surface::DrawFilledRect(int x0,int y0,int x1,int y1) +{ + // trick to draw filled rectangles using current background color + RECT rect = { x0, y0, x1, y1}; + ExtTextOut(PLAT(_currentContextPanel)->hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); +} + +void CWin32Surface::DrawFilledRectArray( IntRect *pRects, int numRects ) +{ + int i; + for( i = 0; i < numRects; i++ ) + { + DrawFilledRect( pRects[i].x0, pRects[i].y0, pRects[i].x1, pRects[i].y1 ); + } +} + +void CWin32Surface::DrawOutlinedRect(int x0,int y0,int x1,int y1) +{ + // draw an outline of a rectangle using 4 filledRect + DrawFilledRect(x0,y0,x1,y0+1); // top + DrawFilledRect(x0,y1-1,x1,y1); // bottom + DrawFilledRect(x0,y0+1,x0+1,y1-1); // left + DrawFilledRect(x1-1,y0+1,x1,y1-1); // right +} + +void CWin32Surface::DrawLine(int x0,int y0,int x1,int y1) +{ + POINT pt[2]; + CONST POINT *p=pt; + pt[0].x=x0; + pt[0].y=y0; + pt[1].x=x1; + pt[1].y=y1; + +// MoveToEx(_currentContextPanel->Plat()->hdc,x0,y0,NULL); +// LineTo(_currentContextPanel->Plat()->hdc,x1,y1); + Polyline(PLAT(_currentContextPanel)->hdc,p , 2); +} + + +void CWin32Surface::DrawPolyLine(int *px, int *py, int numPoints) +{ + POINT *pt; + + pt = (POINT *)malloc(sizeof(POINT) * numPoints); + if(pt) + { + for(int i=0;i<numPoints;i++) + { + pt[i].x= px[i]; + pt[i].y= py[i]; + } + + Polyline(PLAT(_currentContextPanel)->hdc, pt , numPoints); + free(pt); + } +} + +void CWin32Surface::DrawSetTextColor(int r,int g,int b,int a) +{ + // set the draw color for lines + SetLineColor(Color(r,g,b,a)); + SetTextColor(PLAT(_currentContextPanel)->hdc,RGB(r,g,b)); +} + +void CWin32Surface::DrawSetTextColor(Color col) +{ + // set the draw color for lines + SetLineColor(col); + // end then for text + DrawSetTextColor(col[0], col[1], col[2], col[3]); +} + +void CWin32Surface::SetLineColor(Color col) +{ + HPEN tmp=pen; + pen = CreatePen(PS_SOLID,0,RGB(col[0], col[1], col[2])); + if(pen) + { + SelectObject(PLAT(_currentContextPanel)->hdc, pen ); + } + // you must delete the pen AFTER a new one is selected + if(tmp) + { + DeleteObject(tmp); + } +} + + +void CWin32Surface::DrawPrintText(const wchar_t *text, int textLen, FontDrawType_t drawType /*= FONT_DRAW_DEFAULT*/) +{ + Assert(text); + if (!text) + return; + + if (textLen < 1) + return; + + for (int i = 0; i < textLen; i++) + { + DrawUnicodeChar(text[i]); + } + +// ExtTextOut(_currentContextPanel->Plat()->hdc, 0, 0, 0, NULL, text, textLen, NULL); +} + +void CWin32Surface::DrawUnicodeString( const wchar_t *pwString, FontDrawType_t drawType /*= FONT_DRAW_DEFAULT*/) +{ + Assert( pwString ); + if ( !pwString ) + return; + + while ( wchar_t ch = *pwString++ ) + { + DrawUnicodeChar( ch ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws single unicode character at the current position with the +// current font & color +//----------------------------------------------------------------------------- +void CWin32Surface::DrawUnicodeChar(wchar_t wch, FontDrawType_t drawType /*= FONT_DRAW_DEFAULT*/) +{ + // set the current font + CWin32Font *winFont = FontManager().GetFontForChar(m_hCurrentFont, wch); + + if (!winFont) + return; + + if (m_pActiveFont != winFont) + { + winFont->SetAsActiveFont(PLAT(_currentContextPanel)->hdc); + m_pActiveFont = winFont; + } + + if (m_bSupportsUnicode) + { + ExtTextOutW(PLAT(_currentContextPanel)->hdc, 0, 0, 0, NULL, &wch, 1, NULL); + } + else + { + char mbcs[6] = { 0 }; + ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); + ExtTextOutA(PLAT(_currentContextPanel)->hdc, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: allocates a new texture id +//----------------------------------------------------------------------------- +int CWin32Surface::CreateNewTextureID( bool procedural ) +{ + //!! hack, arbitrary base + static int staticBindIndex = 2700; + return staticBindIndex++; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the texture id has a valid texture bound to it +//----------------------------------------------------------------------------- +bool CWin32Surface::IsTextureIDValid(int id) +{ + return (GetTextureById(id) != NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: clear up alloced resources on a texture +//----------------------------------------------------------------------------- +void CWin32Surface::FreeTextureData( vgui::Texture *pTexture ) +{ + if ( pTexture->_bitmap ) + { + ::DeleteObject( pTexture->_bitmap ); + } + +// if ( pTexture->m_bitmapScaled ) +// { +// ::DeleteObject( pTexture->m_bitmapScaled ); +// } + + if ( pTexture->_maskBitmap ) + { + ::DeleteObject( pTexture->_maskBitmap ); + } + + if ( pTexture->_icon ) + { + ::DestroyIcon( pTexture->_icon ); + } + +// if ( pTexture->rgba ) +// delete[] pTexture->rgba; +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the texture id has a valid texture bound to it +//----------------------------------------------------------------------------- +bool CWin32Surface::DeleteTextureByID(int id) +{ +#if defined( PS3OVERLAYUI_EXPORTS ) + AUTO_LOCK( m_MutexTextureData ); +#endif + + Texture findTex = { id }; + int index = m_VGuiSurfaceTextures.Find(findTex); + if (m_VGuiSurfaceTextures.IsValidIndex(index)) + { + Texture *texture = &m_VGuiSurfaceTextures[index]; + + FreeTextureData( texture ); + + m_VGuiSurfaceTextures.RemoveAt(index); + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: does nothing, since we don't need this optimization in win32 +//----------------------------------------------------------------------------- +void CWin32Surface::DrawFlushText() +{ +} + + + +//----------------------------------------------------------------------------- +// Purpose: create a html helper object +//----------------------------------------------------------------------------- +IHTML *CWin32Surface::CreateHTMLWindow(vgui::IHTMLEvents *events, VPANEL context ) +{ + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: delete an html helper object +//----------------------------------------------------------------------------- +void CWin32Surface::DeleteHTMLWindow(IHTML *htmlwin) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: tell a html window to update its backing texture +//----------------------------------------------------------------------------- +void CWin32Surface::PaintHTMLWindow(IHTML *htmlwin) +{ +} + + +//----------------------------------------------------------------------------- +void CWin32Surface::SetAllowHTMLJavaScript( bool state ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: sets the current active texture +//----------------------------------------------------------------------------- +void CWin32Surface::DrawSetTexture(int id) +{ + Texture *texture = GetTextureById(id); + m_pCurrentTexture = texture; +} + +HBITMAP staticCreateBitmapHandle(int wide, int tall, HDC hdc, int bpp, void **dib); +//----------------------------------------------------------------------------- +// Purpose: maps a texture from memory to an id, and uploads it into the engine +//----------------------------------------------------------------------------- +void CWin32Surface::DrawSetTextureRGBA(int id,const unsigned char* rgba,int wide,int tall, int hardwareFilter, bool forceReload) +{ + DrawSetTextureRGBAEx( id, rgba, wide, tall, IMAGE_FORMAT_RGBA8888 ); +} + +//----------------------------------------------------------------------------- +// Purpose: maps a texture from memory to an id, and uploads it into the engine +//----------------------------------------------------------------------------- +void CWin32Surface::DrawSetTextureRGBAEx(int id,const unsigned char* rgba,int wide,int tall, ImageFormat imageFormat ) +{ + Texture *texture = GetTextureById(id); + + if (texture) + { + if (texture->_bitmap) + { + ::DeleteObject(texture->_bitmap); + } + + if (texture->_maskBitmap) + { + ::DeleteObject(texture->_maskBitmap); + } + } + if (!texture) + { + // allocate a new texture + texture = AllocTextureForId(id); + memset(texture, 0, sizeof(Texture)); + } + + { + // no texture or forced load the new texture + texture->_id = id; + texture->_filename =NULL; + + texture->_wide = wide; + texture->_tall = tall; + texture->_icon = NULL; + texture->_dib = NULL; + texture->_bitmap = staticCreateBitmapHandle(texture->_wide, + texture->_tall, PLAT(_currentContextPanel)->hdc, 32, &texture->_dib ); + + // copy over the texture data + memcpy(texture->_dib,rgba,wide*tall*4); + + } + + m_pCurrentTexture = texture; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : id - +// *filename - +// maxlen - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWin32Surface::DrawGetTextureFile(int id, char *filename, int maxlen ) +{ + Texture *texture = GetTextureById(id); + if ( !texture ) + return false; + + Q_strncpy( filename, texture->_filename, maxlen ); + return true; +} + +int CWin32Surface::DrawGetTextureId( char const *filename ) +{ + int i = m_VGuiSurfaceTextures.FirstInorder(); + while ( i != m_VGuiSurfaceTextures.InvalidIndex() ) + { + Texture *texture = &m_VGuiSurfaceTextures[i]; + if ( !Q_stricmp( filename, texture->_filename ) ) + return texture->_id; + + i = m_VGuiSurfaceTextures.NextInorder( i ); + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Maps a texture file to an id, and makes it the current drawing texture +// tries to load as a .tga first, and if not found as a .bmp +//----------------------------------------------------------------------------- +void CWin32Surface::DrawSetTextureFile(int id, const char *filename, int hardwareFilter, bool forceReload /*= false*/) +{ + Texture *texture = GetTextureById(id); + + if (!texture || stricmp(filename, texture->_filename) || forceReload ) + { + // no texture, or the filename is different; load the new texture + if (!texture) + { + // allocate a new texture + texture = AllocTextureForId(id); + memset(texture, 0, sizeof(Texture)); + } + if (texture) + { + if (texture->_bitmap) + { + ::DeleteObject(texture->_bitmap); + } + + if (texture->_maskBitmap) + { + ::DeleteObject(texture->_maskBitmap); + } + } + texture->_id = id; + texture->_filename = filename; + + // try and find the file + bool success = LoadTGA(texture, filename); + if (!success) + { + // strip off the vgui/ and try again + const char *psz = Q_stristr(filename, "vgui/"); + if (psz) + { + success = LoadTGA(texture, filename + strlen("vgui/")); + } + } + + if (!success) + { + Msg("Error: texture file '%s' does not exist or is invalid\n", filename); + return; + } + } + + m_pCurrentTexture = texture; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::DrawTexturedRect(int x0,int y0,int x1,int y1) +{ + if (m_pCurrentTexture == null) + { + return; + } + + if (PLAT(_currentContextPanel)->textureDC == null) + { + return; + } + + HBITMAP bitmap = m_pCurrentTexture->_bitmap; + int wide = m_pCurrentTexture->_wide; + int tall = m_pCurrentTexture->_tall; + + HGDIOBJ oldObject; + + if (m_pCurrentTexture->_bMask) + { + HBITMAP bitmap_mask = m_pCurrentTexture->_maskBitmap; + + // draw the mask first to clear out the background to black + oldObject = ::SelectObject(PLAT(_currentContextPanel)->textureDC, bitmap_mask); + ::StretchBlt(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,SRCAND); + + // draw over the 'black areas' with the bitmap + ::SelectObject(PLAT(_currentContextPanel)->textureDC, bitmap); + ::StretchBlt(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,SRCPAINT); + } + else + { + oldObject = ::SelectObject(PLAT(_currentContextPanel)->textureDC, bitmap); + ::StretchBlt(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,SRCCOPY); + } + ::SelectObject(PLAT(_currentContextPanel)->textureDC, oldObject); + +// test code, should be used in win98/win2k for true alpha +// BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 255, 0 }; +// ::AlphaBlend(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,blendFunction); +} + +//----------------------------------------------------------------------------- +// Purpose: Called by vgui to get texture dimensions +//----------------------------------------------------------------------------- +void CWin32Surface::DrawGetTextureSize(int id, int &wide, int &tall) +{ + Texture *texture = GetTextureById(id); + + if (!texture) + return; + + wide = texture->_wide; + tall = texture->_tall; +} + +#pragma pack(1) +typedef struct +{ + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} tga_header_t; +#pragma pack() + +HBITMAP staticCreateBitmapHandle(int wide, int tall, HDC hdc, int bpp, void **dib) +{ + BITMAPINFOHEADER bitmapInfoHeader; + memset(&bitmapInfoHeader, 0, sizeof(bitmapInfoHeader)); + bitmapInfoHeader.biSize = sizeof(bitmapInfoHeader); + bitmapInfoHeader.biWidth = wide; + bitmapInfoHeader.biHeight = -tall; + bitmapInfoHeader.biPlanes = 1; + bitmapInfoHeader.biBitCount = bpp; + bitmapInfoHeader.biCompression = BI_RGB; + + HBITMAP hRet; + hRet = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfoHeader, DIB_RGB_COLORS, dib, 0, 0); + if ( !hRet ) + Error( "staticCreateBitmapHandle: can't create DIB" ); + + return hRet; +} + +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWin32Surface::LoadBMP(Texture *texture, const char *filename) +{ + // try load the tga + char buf[1024]; + _snprintf(buf, sizeof(buf), "%s.bmp", filename); + + FileHandle_t file = g_pFullFileSystem->Open(buf, "rb", NULL); + if (!file) + return false; + + bool success = false; + + // Parse bitmap + BITMAPFILEHEADER bmfHeader; + DWORD dwBitsSize, dwFileSize; + LPBITMAPINFO lpbmi; + + dwFileSize = g_pFullFileSystem->Size( file ); + + g_pFullFileSystem->Read( &bmfHeader, sizeof(bmfHeader), file ); + + if (bmfHeader.bfType == DIB_HEADER_MARKER) + { + dwBitsSize = dwFileSize - sizeof(bmfHeader); + + HGLOBAL hDIB = ::GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize ); + char *pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB); + { + int i, j; + + g_pFullFileSystem->Read(pDIB, dwBitsSize, file ); + + lpbmi = (LPBITMAPINFO)pDIB; + + // we now have a block of memory, rgba + // throw a bitmap header on it and register it in windows + texture->_wide = lpbmi->bmiHeader.biWidth; + texture->_tall = lpbmi->bmiHeader.biHeight; + texture->_icon = NULL; + texture->_bMask = false; + texture->_bitmap = staticCreateBitmapHandle( + texture->_wide, + texture->_tall, + PLAT(_currentContextPanel)->hdc, + 32, &texture->_dib ); + + unsigned char *rgba = (unsigned char *)( pDIB + sizeof( BITMAPINFOHEADER ) + 256 * sizeof( RGBQUAD ) ); + + // Copy raw data + for (j = 0; j < texture->_tall; j++) + { + for (i = 0; i <texture->_wide; i++) + { + int y = (texture->_tall - j - 1); + + int offs = ( y * texture->_wide + i); + int offsdest = (j * texture->_wide + i) * 4; + unsigned char *src = ((unsigned char *)rgba) + offs; + char *dst = ((char*)texture->_dib) + offsdest; + + dst[0] = lpbmi->bmiColors[ *src ].rgbRed; + dst[1] = lpbmi->bmiColors[ *src ].rgbGreen; + dst[2] = lpbmi->bmiColors[ *src ].rgbBlue; + dst[3] = (unsigned char)255; + } + } + + success = true; + } + + ::GlobalUnlock( hDIB); + ::GlobalFree((HGLOBAL) hDIB); + } + + g_pFullFileSystem->Close(file); + + return success; + + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWin32Surface::LoadTGA(Texture *texture, const char *filename) +{ + bool invertAlpha = false; + + // try load the tga + char buf[1024]; + _snprintf(buf, sizeof(buf), "%s.tga", filename); + + FileHandle_t file = g_pFullFileSystem->Open(buf, "rb", NULL); + if (!file) + { + return LoadBMP( texture, filename ); + } + + // read the header + tga_header_t tgaHeader; + g_pFullFileSystem->Read(&tgaHeader, sizeof(tgaHeader), file); + + if (tgaHeader.image_type != 2 && tgaHeader.image_type != 10) + { + g_pIVgui->DPrintf2("Error: texture file '%s' has invalid image_type %d\n", filename, tgaHeader.image_type); + return false; + } + + if (tgaHeader.colormap_type != 0) + { + return false; + } + + if (tgaHeader.pixel_size != 24 && tgaHeader.pixel_size !=32) + { + return false; + } + + if (tgaHeader.id_length != 0) + { + g_pFullFileSystem->Seek( file, tgaHeader.id_length, FILESYSTEM_SEEK_CURRENT ); + } + + // allocate memory for the destination + uchar *rgba = (unsigned char *)malloc(tgaHeader.width * tgaHeader.height * 4); + + int column, row; + uchar *ptr; + bool bMask = false; + + if (tgaHeader.image_type == 2) + { + for (row = tgaHeader.height - 1; row >= 0; row--) + { + ptr = ((uchar *)rgba) + (row * tgaHeader.width * 4); + for (column = 0; column < tgaHeader.width; column++) + { + switch (tgaHeader.pixel_size) + { + case 24: + { + g_pFullFileSystem->Read(ptr + 2, 1, file); + g_pFullFileSystem->Read(ptr + 1, 1, file); + g_pFullFileSystem->Read(ptr + 0, 1, file); + ptr[3] = 255; + + if (invertAlpha) + { + ptr[3] = 0; + } + ptr += 4; + break; + } + case 32: + { + g_pFullFileSystem->Read(ptr + 2, 1, file); + g_pFullFileSystem->Read(ptr + 1, 1, file); + g_pFullFileSystem->Read(ptr + 0, 1, file); + g_pFullFileSystem->Read(ptr + 3, 1, file); + + if (!invertAlpha) + { + // if it's 0, then it's going to be zeroed out + if (ptr[3] == 0) + { + ptr[3] = 255; + } + else + { + // anything besides 0 will be shown + ptr[3] = 0; + } + //ptr[3] = 255 - ptr[3]; + } + bMask = true; + + ptr += 4; + break; + } + } + } + } + } + else + { + uchar packetHeader, packetSize, j, color[4]; + + for(row = tgaHeader.height - 1; row >= 0; row--) + { + ptr = ((uchar*)rgba) + row * tgaHeader.width * 4; + for(column=0;column<tgaHeader.width;) + { + g_pFullFileSystem->Read(&packetHeader, 1, file); + packetSize = 1 + (packetHeader & 0x7f); + + if (packetHeader & 0x80) + { + switch (tgaHeader.pixel_size) + { + case 24: + { + g_pFullFileSystem->Read(color + 2, 1, file); + g_pFullFileSystem->Read(color + 1, 1, file); + g_pFullFileSystem->Read(color + 0, 1, file); + + if (invertAlpha) + { + color[3] = 0; + } + else + { + color[3] = 255; + } + + break; + } + case 32: + { + g_pFullFileSystem->Read(color + 2, 1, file); + g_pFullFileSystem->Read(color + 1, 1, file); + g_pFullFileSystem->Read(color + 0, 1, file); + g_pFullFileSystem->Read(color + 3, 1, file); + + bMask = true; + if (invertAlpha) + { + color[3] = 255 - color[3]; + } + + break; + } + } + + for (j = 0; j < packetSize; j++) + { + ptr[0] = color[0]; + ptr[1] = color[1]; + ptr[2] = color[2]; + ptr[3] = color[3]; + ptr += 4; + column++; + if (column == tgaHeader.width) + { + column = 0; + if (row > 0) + { + row--; + } + else + { + goto breakOut; + } + ptr = ((uchar*)rgba) + (row * tgaHeader.width * 4); + } + } + } + else + { + for (j = 0; j < packetSize; j++) + { + switch (tgaHeader.pixel_size) + { + case 24: + { + g_pFullFileSystem->Read(ptr + 2, 1, file); + g_pFullFileSystem->Read(ptr + 1, 1, file); + g_pFullFileSystem->Read(ptr + 0, 1, file); + + ptr[3] = 255; + + if (invertAlpha) + { + ptr[3] = 0; + } + else + { + ptr[3] = 255; + } + + ptr += 4; + break; + } + case 32: + { + g_pFullFileSystem->Read(ptr + 2, 1, file); + g_pFullFileSystem->Read(ptr + 1, 1, file); + g_pFullFileSystem->Read(ptr + 0, 1, file); + g_pFullFileSystem->Read(ptr + 3, 1, file); + + if (invertAlpha) + { + ptr[3]=255-ptr[3]; + } + + ptr += 4; + break; + } + } + column++; + if (column == tgaHeader.width) + { + column = 0; + if(row > 0) + { + row--; + } + else + { + goto breakOut; + } + ptr = ((uchar*)rgba) + row * tgaHeader.width * 4; + } + } + } + } + } + breakOut:; + } + + g_pFullFileSystem->Close(file); + + + // we now have a block of memory, rgba + // throw a bitmap header on it and register it in windows + texture->_wide = tgaHeader.width; + texture->_tall = tgaHeader.height; + texture->_icon = NULL; + texture->_bitmap = staticCreateBitmapHandle(tgaHeader.width, tgaHeader.height, PLAT(_currentContextPanel)->hdc, 32, &texture->_dib); + texture->_bMask = bMask; + if (bMask) + texture->_maskBitmap = staticCreateBitmapHandle(tgaHeader.width, tgaHeader.height, PLAT(_currentContextPanel)->hdc, 32, &texture->_maskDib); + else + texture->_maskBitmap = NULL; + + for (int j = 0; j < texture->_tall; j++) + { + for (int i = 0; i < texture->_wide; i++) + { + int offs = (j * texture->_wide + i) * 4; + char *src = ((char*)rgba) + offs; + char *dst = ((char*)texture->_dib) + offs; + + if (bMask) + { + char *maskDst = ((char*) texture->_maskDib) + offs; + + // if there's value here, then it needs to be cleared + if (src[3]) + { + // mask will be & with background, so it needs to be 0xFF + maskDst[0] = maskDst[1] = maskDst[2] = maskDst[3] = -1;; + + // clear the art in the bitmap because it's not going to display and will be | with background + dst[0] = dst[1] = dst[2] = dst[3] = 0; + } + else + { + // this will clear the background before drawing this bitmap + maskDst[0] = maskDst[1] = maskDst[2] = maskDst[3] = 0x00; + + // copy over the art + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[3]; + } + } + else + { + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[3]; + } + } + } + + free(rgba); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: brings the current surface to the foreground +//----------------------------------------------------------------------------- +void CWin32Surface::BringToFront(VPANEL panel) +{ + // force the panel to the top of the windows z-order + panel = GetContextPanelForChildPanel(panel); + if (panel && PLAT(panel)) + { + // this should trigger a WM_SETFOCUS message which will move the window to the top + ::SetActiveWindow(PLAT(panel)->hwnd); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: puts the thread that created the specified window into the foreground +// and activates the window. +//----------------------------------------------------------------------------- +void CWin32Surface::SetForegroundWindow(VPANEL panel) +{ + if (panel && PLAT(panel)) + { + ::SetForegroundWindow(PLAT(panel)->hwnd); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::MovePopupToFront(VPANEL panel) +{ + _popupList.MoveElementToEnd(panel); + + g_pIVgui->PostMessage(panel, new KeyValues("OnMovedPopupToFront"), NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::MovePopupToBack(VPANEL panel) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: +// HWND_TOPMOST - Places the window above all non-topmost windows. +// The window maintains its topmost position even when it is deactivated. +// HWND_NOTOPMOST - Places the window above all non-topmost windows (that is, behind +// all topmost windows). This flag has no effect if the window is already a non-topmost window. +//----------------------------------------------------------------------------- +void CWin32Surface::SetAsTopMost(VPANEL panel, bool state) +{ + panel = GetContextPanelForChildPanel(panel); + DWORD style=SWP_NOMOVE|SWP_NOSIZE; + + if (PLAT(panel)->disabled) + { + style |= SWP_NOACTIVATE; + } + + if (state) + { + SetFocus(PLAT(panel)->hwnd); + SetWindowPos(PLAT(panel)->hwnd,HWND_TOPMOST,0,0,0,0,style ); + } + else + { + SetWindowPos(PLAT(panel)->hwnd,HWND_NOTOPMOST,0,0,0,0,style ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::SetPanelVisible(VPANEL panel, bool visible) +{ + // if the panel has an attached window we need to set it's style + if (PLAT(panel)) + { + if (visible) + { + // show the window + ::ShowWindow(PLAT(panel)->hwnd, SW_SHOWNA); + } + else + { + // hide the window + ::ShowWindow(PLAT(panel)->hwnd, SW_HIDE); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Flashes the window icon in the taskbar, to get the users attention +// Input : flashCount - number of times to flash the window +//----------------------------------------------------------------------------- +void CWin32Surface::FlashWindow(VPANEL panel, bool state) +{ + if (!PLAT(panel)) + return; + + ::FlashWindow(PLAT(panel)->hwnd, state); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::SetTopLevelFocus(VPANEL panel) +{ + // this is handled by WM_FOCUS messages instead of directly +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::SetMinimized(VPANEL panel, bool state) +{ + if (PLAT(panel)) + { + if (state) + { + ::ShowWindow(PLAT(panel)->hwnd, SW_MINIMIZE); + } + else + { + ::ShowWindow(PLAT(panel)->hwnd, SW_SHOWNORMAL); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if the window is minimized +//----------------------------------------------------------------------------- +bool CWin32Surface::IsMinimized(VPANEL panel) +{ + if (PLAT(panel)->hwnd) + { + return ::IsIconic(PLAT(panel)->hwnd); + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::SetTitle(VPANEL panel, const wchar_t *title) +{ + panel = GetContextPanelForChildPanel(panel); + if (panel) + { + if (m_bSupportsUnicode) + { + SetWindowTextW(PLAT(panel)->hwnd, title); + } + else + { + char mbcs[512]; + ::WideCharToMultiByte(CP_ACP, 0, title, -1, mbcs, sizeof(mbcs), NULL, NULL); + SetWindowTextA(PLAT(panel)->hwnd, mbcs); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::SetAsToolBar(VPANEL panel, bool state) +{ + panel = GetContextPanelForChildPanel(panel); + if (panel && PLAT(panel)) + { + if (state) + { + ::SetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE, ::GetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW); + } + else + { + ::SetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE, ::GetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE) & ~WS_EX_TOOLWINDOW); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CWin32Surface::GetPopupCount() +{ + return _popupList.GetCount(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CWin32Surface::GetPopup(int index) +{ + return _popupList[index]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::CreatePopup(VPANEL panel, bool minimised, bool showTaskbarIcon, bool disabled, bool mouseInput, bool kbInput ) +{ + if (((VPanel *)panel)->IsPopup() && PLAT(panel)) + { + // it's already a popup so bail + return; + } + + // make sure it's in the hierarchy + if (!((VPanel *)panel)->GetParent() && panel != _embeddedPanel) + { + ((VPanel *)panel)->SetParent((VPanel *)_embeddedPanel); + } + + int x,y,wide,tall; + ((VPanel *)panel)->GetPos(x, y); + ((VPanel *)panel)->GetSize(wide, tall); + ((VPanel *)panel)->SetPopup(true); + ((VPanel *)panel)->SetKeyBoardInputEnabled(kbInput); + ((VPanel *)panel)->SetMouseInputEnabled(mouseInput); + + // find our parent window if we have one + HWND hwndParent = NULL; + VPANEL pParent = GetContextPanelForChildPanel(panel); + if (pParent && pParent != _embeddedPanel) + { + hwndParent = PLAT(pParent)->hwnd; + } + + //create the window and initialize platform specific data + //window is initial a popup and not visible + //when ApplyChanges is called the window will be shown unless + //it SetVisible(false) is called + SurfacePlat *plat = new SurfacePlat; + ((VPanel *)panel)->SetPlat(plat); + + // WS_SYSMENU and WS_MINIMIZEBOX flags are there simply to make icon appear in taskbar + // WS_EX_TOOLWINDOW hides the taskbar/ALT-TAB button + DWORD style=0,style_ex=0; + if (!showTaskbarIcon) + { + style = WS_POPUP | WS_CLIPCHILDREN; + style_ex= WS_EX_TOOLWINDOW; + if (!hwndParent) + { + hwndParent = PLAT(_embeddedPanel)->hwnd; + } + } + else + { + style = WS_POPUP | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN; + } + if (panel != _embeddedPanel && ((VPanel *)panel)->IsVisible()) + { + style |= WS_VISIBLE; + } + + if(disabled) + { + style |= WS_DISABLED; + } + + if ( minimised ) + { + style |= WS_MINIMIZE; + } + + plat->hwnd = CreateWindowEx(style_ex, "Surface", "", style, x, y, wide, tall, hwndParent, NULL, GetModuleHandle(NULL), NULL); + + plat->clipRgn = CreateRectRgn(0,0,64,64); + plat->hdc = CreateCompatibleDC(NULL); + plat->hwndDC = NULL; + plat->bitmap = null; + plat->bitmapSize[0] = 0; + plat->bitmapSize[1] = 0; + plat->isFullscreen = false; + plat->embeddedPanel = (VPanel *)panel; + plat->disabled=disabled; + plat->notifyIcon = NULL; + plat->textureDC = NULL; + + ::SetBkMode(plat->hdc, TRANSPARENT); + ::SetWindowLong(plat->hwnd, GWL_USERDATA, (LONG)g_pIVgui->PanelToHandle(panel)); + ::SetTextAlign(plat->hdc, TA_LEFT | TA_TOP | TA_UPDATECP); + + if (!((VPanel *)panel)->IsVisible() || panel == _embeddedPanel) + { + SetPanelVisible(panel, false); + } + + // create the context + RecreateContext(panel); + + ::RegisterDragDrop(plat->hwnd, &staticDragDropTarget); + + // add the panel to the popup list + if (!_popupList.HasElement(panel)) + { + _popupList.AddElement(panel); + } + else + { + // somehow getting added twice, fundamental problem + _asm int 3; + } + + // hack, force a windows sound to be played + // the first time PlaySound is called there is 300ms hang, so this forces it to happen at startup + // instead of while the user is trying to do something + // we can't do this before a window created else it'll hang + static bool first = true; + if (first) + { + // double startTime = system()->GetCurrentTime(); + ::PlaySoundA("", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP | SND_NOWAIT); + // double endTime = system()->GetCurrentTime(); + // ivgui()->DPrintf2("PlaySound() : %dms\n", (int)((endTime - startTime) * 1000)); + first = false; + } + + //!! hack to set a valid current context panel + SetCurrentContextPanel(_embeddedPanel); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a panel is created +//----------------------------------------------------------------------------- +void CWin32Surface::AddPanel(VPANEL panel) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a panel gets deleted +//----------------------------------------------------------------------------- +void CWin32Surface::ReleasePanel(VPANEL panel) +{ + _popupList.RemoveElement(panel); + + if (panel == _currentContextPanel) + { + _currentContextPanel = _embeddedPanel; + } + + if (PLAT(panel)) + { + SurfacePlat *plat = PLAT(panel); + + // release drag/drop + ::RevokeDragDrop(plat->hwnd); + + // remove notify icons + if (plat->notifyIcon) + { + SetNotifyIcon(panel, NULL, NULL, NULL); + } + + // hide the panel + SetPanelVisible(panel, false); + + // free all the windows/bitmap/DC handles we are using + ::SetWindowLong(plat->hwnd, GWL_USERDATA, (LONG)-1); + ::SetWindowPos(plat->hwnd, HWND_BOTTOM, 0, 0, 1, 1, SWP_NOREDRAW|SWP_HIDEWINDOW); + + // free the window context + if ( plat->bitmap ) + { + ::DeleteObject( plat->bitmap ); + } + + if ( plat->textureDC ) + { + ::DeleteDC( plat->textureDC ); + } + + if (plat->hwndDC) + { + ::ReleaseDC( plat->hwnd, plat->hwndDC ); + } + ::DeleteDC( plat->hdc ); + ::DestroyWindow( plat->hwnd ); + ::DeleteObject( plat->clipRgn ); + + // don't free staticWndclassAtom, because it is shared amongst surfaces, + // and is automatically freed when the application terminates + + delete plat; + ((VPanel *)panel)->SetPlat(NULL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Applies any changes to the panel into the underline wnidow +//----------------------------------------------------------------------------- +bool CWin32Surface::RecreateContext(VPANEL panel) +{ + if (panel && PLAT(panel)) + { + SurfacePlat *plat = PLAT(panel); + int wide,tall; + ((VPanel *)panel)->GetSize(wide,tall); + + // rebuild bitmap only if necessary + // simple scheme to prevent excessive allocations by allocating only when + // bigger. It also adds in 100 extra for subsequent sizings + // it will also realloc if the size is 200 smaller to shrink memory usage + if ((wide > plat->bitmapSize[0]) + || (tall > plat->bitmapSize[1]) + || (wide < (plat->bitmapSize[0] - 200)) + || (tall < (plat->bitmapSize[1] - 200))) + { + if (plat->bitmap != null) + { + ::DeleteObject(plat->bitmap); + } + + plat->hwndDC = GetDC(plat->hwnd); + + plat->bitmap = ::CreateCompatibleBitmap(plat->hwndDC, wide + 100, tall + 100); + plat->bitmapSize[0] = wide + 100; + plat->bitmapSize[1] = tall + 100; + + if (plat->textureDC) + { + ::DeleteDC(plat->textureDC); + } + + ::SelectObject(plat->hdc, plat->bitmap); + plat->textureDC = ::CreateCompatibleDC(plat->hdc); + + ::ReleaseDC(plat->hwnd, plat->hwndDC); + plat->hwndDC = NULL; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::EnableMouseCapture(VPANEL panel, bool state) +{ + VPANEL contextPanel = GetContextPanelForChildPanel(panel); + if (state) + { + ::SetCapture(PLAT(contextPanel)->hwnd); + } + else + { + ::ReleaseCapture(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWin32Surface::ShouldPaintChildPanel(VPANEL childPanel) +{ + // don't Paint children as part of the normal process, handle them in with WM_PAINT messages instead + return !((VPanel *)childPanel)->IsPopup(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called after a Paint to display the new buffer +//----------------------------------------------------------------------------- +void CWin32Surface::SwapBuffers(VPANEL panel) +{ + if (PLAT(panel)) + { + START_TIMER(); + + SurfacePlat *plat = PLAT(panel); + + int wide,tall; + ((VPanel *)panel)->GetSize(wide,tall); + + plat->hwndDC = ::GetDC(plat->hwnd); + + // reset origin and clipping then blit + ::SetRectRgn(plat->clipRgn, 0, 0, wide, tall); + ::SelectObject(plat->hdc, plat->clipRgn); + ::SetViewportOrgEx(plat->hdc, 0, 0, NULL); + ::BitBlt(plat->hwndDC, 0, 0, wide, tall, plat->hdc, 0, 0, SRCCOPY); + + ::ReleaseDC(plat->hwnd, plat->hwndDC); + plat->hwndDC = NULL; + + END_TIMER("SwapBuffers time: %.2fms\n"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame to change to window state if necessary +//----------------------------------------------------------------------------- +void CWin32Surface::ApplyChanges() +{ + for (int i = 0; i < GetPopupCount(); i++) + { + VPANEL panel = GetPopup(i); + + if (!PLAT(panel)) + continue; + + SurfacePlat *Plat = PLAT(panel); + + // force the main panel to be invisible + if (panel == GetEmbeddedPanel()) + { + ::ShowWindow(Plat->hwnd, SW_HIDE); + continue; + } + + // hide and skip by invisible panels + if (!((VPanel *)panel)->IsVisible()) + { + ::ShowWindow(Plat->hwnd, SW_HIDE); + continue; + } + + RECT rect; + int x, y, wide, tall, sx, sy, swide, stall; + + // get how big the win32 window + ::GetWindowRect(Plat->hwnd, &rect); + sx = rect.left; + sy = rect.top; + swide = rect.right-rect.left; + stall = rect.bottom-rect.top; + + // how big is the embedded VPanel + ((VPanel *)panel)->GetPos(x, y); + ((VPanel *)panel)->GetSize(wide, tall); + + // if they are not the same, then adjust the win32 window so it is + if ((x != sx) || (y != sy) || (wide != swide) || (tall != stall)) + { + ::SetWindowPos(Plat->hwnd, null, x, y, wide, tall, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); + if ( sx > 0 || sy > 0 ) // only message for moves that are on the screen + { + g_pIVgui->PostMessage(panel, new KeyValues("Move"), NULL ); + } + } + + // check to see if the win32 window is visible + if (::GetWindowLong(Plat->hwnd, GWL_STYLE) & WS_VISIBLE) + { + //check to see if embedded VPanel is not visible, if so then hide the win32 window + if (!((VPanel *)panel)->IsVisible()) + { + ::ShowWindow(Plat->hwnd, SW_HIDE); + } + } + else // win32 window is hidden + { + //check to see if embedded VPanel is visible, if so then show the win32 window + if (((VPanel *)panel)->IsVisible()) + { + ::ShowWindow(Plat->hwnd, SW_SHOWNA); + } + } + + //if the win32 window changed size and is visible, then the context needs to be recreated + if (((wide != swide) || (tall != stall)) && ((VPanel *)panel)->IsVisible()) + { + RecreateContext(panel); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: recurses the panels calculating absolute positions +// parents must be solved before children +//----------------------------------------------------------------------------- +void CWin32Surface::InternalSolveTraverse(VPANEL panel) +{ + // solve the parent + ((VPanel *)panel)->Solve(); + + // now we can solve the children + for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) + { + VPanel *child = ((VPanel *)panel)->GetChild(i); + if (child->IsVisible()) + { + InternalSolveTraverse((VPANEL)child); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: recurses the panels giving them a chance to do a user-defined think, +// PerformLayout and ApplySchemeSettings +// must be done child before parent +//----------------------------------------------------------------------------- +void CWin32Surface::InternalThinkTraverse(VPANEL panel) +{ + // parent + ((VPanel *)panel)->Client()->Think(); + + // then the children... + for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) + { + VPanel *child = ((VPanel *)panel)->GetChild(i); + if (child->IsVisible()) + { + InternalThinkTraverse((VPANEL)child); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: recurses the panels giving them a chance to do a ApplySchemeSettings +// must be done child before parent +//----------------------------------------------------------------------------- +void CWin32Surface::InternalSchemeSettingsTraverse(VPANEL panel, bool forceApplySchemeSettings) +{ + // think the children... + for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) + { + VPanel *child = ((VPanel *)panel)->GetChild(i); + if ( forceApplySchemeSettings || child->IsVisible() ) + { + InternalSchemeSettingsTraverse((VPANEL)child, forceApplySchemeSettings); + } + } + + // ... then the parent + ((VPanel *)panel)->Client()->PerformApplySchemeSettings(); +} + +//----------------------------------------------------------------------------- +// Purpose: Walks through the panel tree calling Solve() on them all, in order +//----------------------------------------------------------------------------- +void CWin32Surface::SolveTraverse(VPANEL panel, bool forceApplySchemeSettings) +{ + // ignore visibility for this + InternalSchemeSettingsTraverse(panel,forceApplySchemeSettings); // do apply scheme settings, child to parent + + if (!((VPanel *)panel)->IsVisible()) + return; + + InternalThinkTraverse(panel); + InternalSolveTraverse(panel); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWin32Surface::PaintTraverse(VPANEL panel) +{ + START_TIMER(); + + ((VPanel *)panel)->Client()->PaintTraverse(false,true); + + END_TIMER("Paint time: %.2fms\n"); +} + +// FIXME: write these functions! +void CWin32Surface::RestrictPaintToSinglePanel(VPANEL panel) +{ +} + +void CWin32Surface::SetModalPanel(VPANEL ) +{ +} + +VPANEL CWin32Surface::GetModalPanel() +{ +return 0; +} + +void CWin32Surface::UnlockCursor() +{ +} + +void CWin32Surface::LockCursor() +{ +} + +void CWin32Surface::SetTranslateExtendedKeys(bool state) +{ +} + +VPANEL CWin32Surface::GetTopmostPopup() +{ + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if the mouse should be visible or not +//----------------------------------------------------------------------------- +void CWin32Surface::CalculateMouseVisible() +{ + int i; + _needMouse = false; + _needKB = false; + + for(i = 0 ; i < g_pSurface->GetPopupCount() ; i++ ) + { + VPanel *pop = (VPanel *)g_pSurface->GetPopup( i ) ; + + bool isVisible=pop->IsVisible(); + VPanel *p= pop->GetParent(); + + while(p && isVisible) + { + if( p->IsVisible()==false) + { + isVisible=false; + break; + } + p=p->GetParent(); + } + + if(isVisible) + { + _needMouse |= pop->IsMouseInputEnabled(); + _needKB |= pop->IsKeyBoardInputEnabled(); + } + } + + if (_needMouse) + { + SetCursor(vgui::dc_arrow); + UnlockCursor(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWin32Surface::NeedKBInput() +{ + return _needKB; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the current cursor +//----------------------------------------------------------------------------- +void CWin32Surface::SetCursor(HCursor cursor) +{ + switch (cursor) + { + case dc_none: + case dc_arrow: + case dc_ibeam: + case dc_hourglass: + case dc_waitarrow: + case dc_crosshair: + case dc_up: + case dc_sizenwse: + case dc_sizenesw: + case dc_sizewe: + case dc_sizens: + case dc_sizeall: + case dc_no: + case dc_hand: + { + _currentCursor = staticDefaultCursor[cursor]; + break; + } + case dc_user: + { + break; + } + case dc_blank: // don't touch the cursor, just stick with what windows is currently using + { + return; + break; + } + } + + ::SetCursor(_currentCursor); +} + +//----------------------------------------------------------------------------- +// Purpose: Forces the window to be redrawn +//----------------------------------------------------------------------------- +void CWin32Surface::Invalidate(VPANEL panel) +{ + panel = GetContextPanelForChildPanel(panel); + if (panel && PLAT(panel) && ((VPanel *)panel)->IsVisible()) + { + // last parm must be false so WM_ERASEBKGND is not generated, that should + // only be generated by windows + InvalidateRect(PLAT(panel)->hwnd, NULL, false); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if any of the windows have focus +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWin32Surface::HasFocus() +{ + HWND focus = ::GetFocus(); + if (!focus) + return false; + + // see if any of the windows on the surface have the focus + for (int i = 0; i < GetPopupCount(); i++) + { + VPANEL panel = GetPopup(i); + if (focus == PLAT(panel)->hwnd) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the cursor is over this surface +// Uses the windows call to do this, instead of doing it procedurally +//----------------------------------------------------------------------------- +bool CWin32Surface::IsWithin(int x,int y) +{ + POINT pnt={x,y}; + HWND hwnd = WindowFromPoint(pnt); + + for (int i = 0; i < GetPopupCount(); i++) + { + if (PLAT(GetPopup(i))->hwnd == hwnd) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: handles a set focus message +//----------------------------------------------------------------------------- +void CWin32Surface::setFocus(VPANEL panel) +{ + // reset the focus to the last focused panel + panel = GetContextPanelForChildPanel(panel); + if (panel && PLAT(panel)) + { + // make sure we have a valid panel + VPANEL focus = panel; + if (focus && ((VPanel *)focus)->HasParent((VPanel *)panel)) + { + // Must be a child of the modal surface, if set + if ( g_pInput->GetAppModalSurface() && !((VPanel *)focus)->HasParent((VPanel *)g_pInput->GetAppModalSurface()) ) + { + // trying to set focus to something that's not the modal panel, set it back + SetForegroundWindow(g_pInput->GetAppModalSurface()); + } + else + { + // make sure focus is at top of list + // the subfocus will be automatically calculated in IInput::RunFrame() + ((VPanel *)focus)->MoveToFront(); + return; + } + } + } + + // let the main panel get focus as the default + if (panel) + { + ((VPanel *)panel)->Client()->RequestFocus(0); + } +} + +struct FontRangeItem_t +{ + const char *name; + int lowerRange; + int upperRange; +}; + +FontRangeItem_t g_FontRanges[] = +{ + { "Basic Latin", 0x0000, 0x007F }, + { "Latin-1 Supplement", 0x0080, 0x00FF }, + { "Latin Extended-A", 0x0100, 0x017F }, + { "Latin Extended-B", 0x0180, 0x024F }, + { "IPA Extensions", 0x0250, 0x02AF }, + { "Spacing Modifier Letters", 0x02B0, 0x02FF }, + { "Combining Diacritical Marks", 0x0300, 0x036F }, + { "Greek", 0x0370, 0x03FF }, + { "Cyrillic", 0x0400, 0x04FF }, + { "Armenian", 0x0530, 0x058F }, + { "Hebrew", 0x0590, 0x05FF }, + { "Arabic", 0x0600, 0x06FF }, + { "Syriac", 0x0700, 0x074F }, + { "Thaana", 0x0780, 0x07BF }, + { "Devanagari", 0x0900, 0x097F }, + { "Bengali", 0x0980, 0x09FF }, + { "Gurmukhi", 0x0A00, 0x0A7F }, + { "Gujarati", 0x0A80, 0x0AFF }, + { "Oriya", 0x0B00, 0x0B7F }, + { "Tamil", 0x0B80, 0x0BFF }, + { "Telugu", 0x0C00, 0x0C7F }, + { "Kannada", 0x0C80, 0x0CFF }, + { "Malayalam", 0x0D00, 0x0D7F }, + { "Sinhala", 0x0D80, 0x0DFF }, + { "Thai", 0x0E00, 0x0E7F }, + { "Lao", 0x0E80, 0x0EFF }, + { "Tibetan", 0x0F00, 0x0FBF }, + { "Myanmar", 0x1000, 0x109F }, + { "Georgian", 0x10A0, 0x10FF }, + { "Hangul Jamo", 0x1100, 0x11FF }, + { "Ethiopic", 0x1200, 0x137F }, + { "Cherokee", 0x13A0, 0x13FF }, + { "Unified Canadian Aboriginal Syllabics", 0x1400, 0x167F }, + { "Ogham", 0x1680, 0x169F }, + { "Runic", 0x16A0, 0x16FF }, + { "Khmer", 0x1780, 0x17FF }, + { "Mongolian", 0x1800, 0x18AF }, + { "Latin Extended Additional", 0x1E00, 0x1EFF }, + { "Greek Extended", 0x1F00, 0x1FFF }, + { "General Punctuation", 0x2000, 0x206F }, + { "Superscripts and Subscripts", 0x2070, 0x209F }, + { "Currency Symbols", 0x20A0, 0x20CF }, + { "Combining Diacritical Marks for Symbols",0x20D0, 0x20FF }, + { "Letterlike Symbols", 0x2100, 0x214F }, + { "Number Forms", 0x2150, 0x218F }, + { "Arrows", 0x2190, 0x21FF }, + { "Mathematical Operators", 0x2200, 0x22FF }, + { "Miscellaneous Technical", 0x2300, 0x23FF }, + { "Control Pictures", 0x2400, 0x243F }, + { "Optical Character Recognition", 0x2440, 0x245F }, + { "Enclosed Alphanumerics", 0x2460, 0x24FF }, + { "Box Drawing", 0x2500, 0x257F }, + { "Block Elements", 0x2580, 0x259F }, + { "Geometric Shapes", 0x25A0, 0x25FF }, + { "Miscellaneous Symbols", 0x2600, 0x26FF }, + { "Dingbats", 0x2700, 0x27BF }, + { "Braille Patterns", 0x2800, 0x28FF }, + { "CJK Radicals Supplement", 0x2E80, 0x2EFF }, + { "KangXi Radicals", 0x2F00, 0x2FDF }, + { "Ideographic Description Characters", 0x2FF0, 0x2FFF }, + { "CJK Symbols and Punctuation", 0x3000, 0x303F }, + { "Hiragana", 0x3040, 0x309F }, + { "Katakana", 0x30A0, 0x30FF }, + { "Bopomofo", 0x3100, 0x312F }, + { "Hangul Compatibility Jamo", 0x3130, 0x318F }, + { "Kanbun", 0x3190, 0x319F }, + { "Bopomofo Extended", 0x31A0, 0x31BF }, + { "Enclosed CJK Letters and Months", 0x3200, 0x32FF }, + { "CJK Compatibility", 0x3300, 0x33FF }, + { "CJK Unified Ideographs Extension A", 0x3400, 0x4DB5 }, + { "CJK Unified Ideographs", 0x4E00, 0x9FFF }, + { "Yi Syllables", 0xA000, 0xA48F }, + { "Yi Radicals", 0xA490, 0xA4CF }, + { "Hangul Syllables", 0xAC00, 0xD7A3 }, + { "-- surrogates --", 0xD800, 0xDBFF }, + { "CJK Compatibility Ideographs", 0xF900, 0xFAFF }, + { "Alphabetic Presentation Forms", 0xFB00, 0xFB4F }, + { "Arabic Presentation Forms-A", 0xFB50, 0xFDFF }, + { "Combining Half Marks", 0xFE20, 0xFE2F }, + { "CJK Compatibility Forms", 0xFE30, 0xFE4F }, + { "Small Form Variants", 0xFE50, 0xFE6F }, + { "Arabic Presentation Forms-B", 0xFE70, 0xFEFF }, + { "Halfwidth and Fullwidth Forms", 0xFF00, 0xFFEF }, + { "Specials", 0xFFF0, 0xFFFF }, +}; + + +//----------------------------------------------------------------------------- +// Purpose: creates a new empty font +//----------------------------------------------------------------------------- +HFont CWin32Surface::CreateFont() +{ + return FontManager().CreateFont(); +} + +//----------------------------------------------------------------------------- +// Purpose: adds glyphs to a font created by CreateFont() +//----------------------------------------------------------------------------- +bool CWin32Surface::SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax) +{ + return FontManager().SetFontGlyphSet(font, windowsFontName, tall, weight, blur, scanlines, flags, nRangeMin, nRangeMax); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the max height of a font +//----------------------------------------------------------------------------- +int CWin32Surface::GetFontTall(HFont font) +{ + return FontManager().GetFontTall(font); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the requested height of a font +//----------------------------------------------------------------------------- +int CWin32Surface::GetFontTallRequested(HFont font) +{ + return FontManager().GetFontTallRequested(font); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the max height of a font +//----------------------------------------------------------------------------- +int CWin32Surface::GetFontAscent(HFont font, wchar_t wch) +{ + return FontManager().GetFontAscent(font,wch); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : font - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWin32Surface::IsFontAdditive(HFont font) +{ + return FontManager().IsFontAdditive(font); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the abc widths of a single character +//----------------------------------------------------------------------------- +void CWin32Surface::GetCharABCwide(HFont font, int ch, int &a, int &b, int &c) +{ + FontManager().GetCharABCwide(font, ch, a, b, c); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the pixel width of a single character +//----------------------------------------------------------------------------- +int CWin32Surface::GetCharacterWidth(HFont font, int ch) +{ + return FontManager().GetCharacterWidth(font, ch); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the area of a text string, including newlines +//----------------------------------------------------------------------------- +void CWin32Surface::GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall) +{ + FontManager().GetTextSize(font, text, wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: adds a custom font file (only supports true type font files (.ttf) for now) +//----------------------------------------------------------------------------- +bool CWin32Surface::AddCustomFontFile(const char *fontName, const char *fontFileName) +{ + char fullPath[ MAX_PATH ]; + g_pFullFileSystem->GetLocalPath(fontFileName, fullPath, sizeof( fullPath )); + m_CustomFontFileNames.AddToTail(fontFileName); + return (::AddFontResource(fullPath) > 0); +} + +//----------------------------------------------------------------------------- +// Purpose: Pre-compiled bitmap font support for game engine - not implemented for GDI +//----------------------------------------------------------------------------- +bool CWin32Surface::AddBitmapFontFile(const char *fontFileName) +{ + Assert( 0 ); + return false; +} +void CWin32Surface::SetBitmapFontName( const char *pName, const char *pFontFilename ) +{ + Assert( 0 ); +} +const char *CWin32Surface::GetBitmapFontName( const char *pName ) +{ + Assert( 0 ); + return NULL; +} +bool CWin32Surface::SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags) +{ + Assert( 0 ); + return false; +} + +void CWin32Surface::PrecacheFontCharacters(HFont font, const wchar_t *pCharacters) +{ + Assert( 0 ); +} + +void CWin32Surface::ClearTemporaryFontCache( void ) +{ + Assert( 0 ); +} + +const char *CWin32Surface::GetFontName( HFont font ) +{ + return FontManager().GetFontName( font ); +} + +const char *CWin32Surface::GetFontFamilyName( HFont font ) +{ + return FontManager().GetFontFamilyName( font ); +} + +void CWin32Surface::DrawSetTextScale(float sx, float sy) +{ + Assert( 0 ); +} +void CWin32Surface::SetPanelForInput( VPANEL vpanel ) +{ + Assert( 0 ); +} + +void CWin32Surface::DrawFilledRectFastFade( int x0, int y0, int x1, int y1, int fadeStartPt, int fadeEndPt, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) +{ + Assert( 0 ); +} + +void CWin32Surface::DrawFilledRectFade( int x0, int y0, int x1, int y1, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) +{ + Assert( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the bounds of the usable workspace area +//----------------------------------------------------------------------------- +void CWin32Surface::GetWorkspaceBounds(int &x, int &y, int &wide, int &tall) +{ + RECT rcScreen; + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, 0); + + x = rcScreen.left; + y = rcScreen.top; + wide = rcScreen.right - x; + tall = rcScreen.bottom - y; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the absolute coordinates of the screen (in screen space) +//----------------------------------------------------------------------------- +void CWin32Surface::GetAbsoluteWindowBounds(int &x, int &y, int &wide, int &tall) +{ + // always work in full window screen space + x = 0; + y = 0; + GetScreenSize(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: Plays a sound +// Input : *fileName - name of the wav file +//----------------------------------------------------------------------------- +void CWin32Surface::PlaySound(const char *fileName) +{ + char localPath[MAX_PATH]; + if (!g_pFullFileSystem->GetLocalPath(fileName, localPath, sizeof(localPath))) + return; + + g_pFullFileSystem->GetLocalCopy(localPath); + ::PlaySoundA(localPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP | SND_NOWAIT); +} + +bool GetIconSize( ICONINFO& iconInfo, int& w, int& h ) +{ + w = h = 0; + + HBITMAP bitmap = iconInfo.hbmColor; + BITMAP bm; + if ( 0 == GetObject((HGDIOBJ)bitmap, sizeof(BITMAP), (LPVOID)&bm) ) + { + return false; + } + + w = bm.bmWidth; + h = bm.bmHeight; + + return true; +} + +class CIconImage : public IImage +{ +public: + CIconImage( HICON hIcon ) : m_hIcon( CopyIcon( hIcon ) ) + { + m_Pos.x = m_Pos.y = 0; + + ICONINFO iconInfo; + if ( 0 != GetIconInfo( m_hIcon, &iconInfo ) ) + { + int w, h; + GetIconSize( iconInfo, w, h ); + m_Size.cx = w; + m_Size.cy = h; + } + else + { + m_Size.cx = 0; + m_Size.cy = 0; + } + } + + // virtual destructor + virtual ~CIconImage() + { + DestroyIcon( m_hIcon ); + } + + // Call to Paint the image + // Image will draw within the current panel context at the specified position + virtual void Paint() + { + if ( !m_hIcon ) + return; + + if ( !m_Size.cx || !m_Size.cy ) + return; + + HDC hdc = PLAT(g_Surface._currentContextPanel)->hdc; + + // Translate position to screen space based on surface state... + int x, y; + x = m_Pos.x; + y = m_Pos.y; + + DrawIconEx + ( + hdc, + x, y, + m_hIcon, + m_Size.cx, m_Size.cy, + 0, + NULL, + DI_NORMAL + ); + } + + // Set the position of the image + virtual void SetPos(int x, int y) + { + m_Pos.x = x; + m_Pos.y = y; + } + + // Gets the size of the content + virtual void GetContentSize(int &wide, int &tall) + { + wide = m_Size.cx; + tall = m_Size.cy; + } + + // Get the size the image will actually draw in (usually defaults to the content size) + virtual void GetSize(int &wide, int &tall) + { + GetContentSize( wide, tall ); + } + + // Sets the size of the image + virtual void SetSize(int wide, int tall) + { + // Nothing + } + + // Set the draw color + virtual void SetColor(Color col) + { + // Nothing + } + + virtual bool Evict() { return false; } + + virtual int GetNumFrames() { return 0; } + virtual void SetFrame( int nFrame ) {} + + virtual HTexture GetID() { return 0; } + virtual void SetRotation( int iRotation ) { return; }; + +private: + + HICON m_hIcon; + POINT m_Pos; + SIZE m_Size; +}; + +static bool ShouldMakeUnique( char const *extension ) +{ + if ( !Q_stricmp( extension, "cur" ) ) + return true; + if ( !Q_stricmp( extension, "ani" ) ) + return true; + return false; +} + +IImage *CWin32Surface::GetIconImageForFullPath( char const *pFullPath ) +{ + IImage *newIcon = NULL; + + SHFILEINFO info = { 0 }; + DWORD_PTR dwResult = SHGetFileInfo( + pFullPath, + 0, + &info, + sizeof( info ), + SHGFI_TYPENAME | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SHELLICONSIZE + ); + if ( dwResult ) + { + if ( info.szTypeName[ 0 ] != 0 ) + { + char ext[ 32 ]; + Q_ExtractFileExtension( pFullPath, ext, sizeof( ext ) ); + + char lookup[ 512 ]; + Q_snprintf( lookup, sizeof( lookup ), "%s", ShouldMakeUnique( ext ) ? pFullPath : info.szTypeName ); + + // Now check the dictionary + unsigned short idx = m_FileTypeImages.Find( lookup ); + if ( idx == m_FileTypeImages.InvalidIndex() ) + { + newIcon = new CIconImage( info.hIcon ); + idx = m_FileTypeImages.Insert( lookup, newIcon ); + } + + newIcon = m_FileTypeImages[ idx ]; + } + + DestroyIcon( info.hIcon ); + } + + return newIcon; +} + +//----------------------------------------------------------------------------- +// Purpose: Handles windows message pump +//----------------------------------------------------------------------------- +void CWin32Surface::RunFrame() +{ + ::MSG msg; + if (!g_pIVgui->GetShouldVGuiControlSleep()) + { + // if vgui doesn't control sleeping, then make sure we don't block in this loop + if (!::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + return; + } + + // always get at least one message + // this means it will block until input + // there is a timer set to force this loop to run at least 20Hz, which should be fine for the desktop + do + { + BOOL ret = ::GetMessage(&msg, NULL, 0, 0); + if (ret == 0) + break; + + if (ret == -1) + { + g_pIVgui->Stop(); + break; + } + + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)); +} + +//----------------------------------------------------------------------------- +// Purpose: cap bits +//----------------------------------------------------------------------------- +bool CWin32Surface::SupportsFeature(SurfaceFeature_e feature) +{ + switch (feature) + { + case ISurface::ESCAPE_KEY: + case ISurface::ANTIALIASED_FONTS: + case ISurface::OPENING_NEW_HTML_WINDOWS: + case ISurface::FRAME_MINIMIZE_MAXIMIZE: + case ISurface::DIRECT_HWND_RENDER: + return true; + + case ISurface::DROPSHADOW_FONTS: + case ISurface::OUTLINE_FONTS: + default: + return false; + }; +} + +//----------------------------------------------------------------------------- +// Purpose: Initializes all the static data for the app +// Should be only called during load time +//----------------------------------------------------------------------------- +void CWin32Surface::initStaticData() +{ + //load up all default cursors, this gets called everytime a Surface is created, but + //who cares + staticDefaultCursor[dc_none] =null; + staticDefaultCursor[dc_arrow] =(HICON)LoadCursor(null,(LPCTSTR)OCR_NORMAL); + staticDefaultCursor[dc_ibeam] =(HICON)LoadCursor(null,(LPCTSTR)OCR_IBEAM); + staticDefaultCursor[dc_hourglass]=(HICON)LoadCursor(null,(LPCTSTR)OCR_WAIT); + staticDefaultCursor[dc_waitarrow]=(HICON)LoadCursor(null,(LPCTSTR)OCR_APPSTARTING); + staticDefaultCursor[dc_crosshair]=(HICON)LoadCursor(null,(LPCTSTR)OCR_CROSS); + staticDefaultCursor[dc_up] =(HICON)LoadCursor(null,(LPCTSTR)OCR_UP); + staticDefaultCursor[dc_sizenwse] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZENWSE); + staticDefaultCursor[dc_sizenesw] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZENESW); + staticDefaultCursor[dc_sizewe] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZEWE); + staticDefaultCursor[dc_sizens] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZENS); + staticDefaultCursor[dc_sizeall] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZEALL); + staticDefaultCursor[dc_no] =(HICON)LoadCursor(null,(LPCTSTR)OCR_NO); + staticDefaultCursor[dc_hand] =(HICON)LoadCursor(null,(LPCTSTR)32649); + + // make and register a very simple Window Class + memset( &staticWndclass,0,sizeof(staticWndclass) ); + staticWndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + staticWndclass.lpfnWndProc = staticProc; + staticWndclass.hInstance = GetModuleHandle(NULL); + + // Get the resource ID of the icon group from the environment...default to 101. + DWORD wIconID = 101; + const char *sIconResourceID = getenv(szSteamBootStrapperIconIdEnvVar); + if ( sIconResourceID ) + { + DWORD wTmpIconID = 0; + if ( sscanf(sIconResourceID, "%u", &wTmpIconID) == 1 && wTmpIconID > 101 ) + { + wIconID = wTmpIconID; + } + } + staticWndclass.hIcon = ::LoadIcon(staticWndclass.hInstance, MAKEINTRESOURCE(wIconID)); + + staticWndclass.lpszClassName = "Surface"; + staticWndclassAtom = ::RegisterClass( &staticWndclass ); + + // register our global Shutdown command + staticShutdownMsg = ::RegisterWindowMessage("ShutdownValvePlatform"); +} + +//----------------------------------------------------------------------------- +// Purpose: our basic user agent string when doing http requests from the client for webkit +//----------------------------------------------------------------------------- +const char *CWin32Surface::GetWebkitHTMLUserAgentString() +{ + return "Valve Client"; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handle switching in and out of "render to fullscreen" mode. We don't +// actually support this mode in tools. +//----------------------------------------------------------------------------- +void CWin32Surface::PushFullscreenViewport() +{ + AssertMsg( false, "Fullscreen viewport mode is unimplemented in CWin32Surface" ); +} + +void CWin32Surface::PopFullscreenViewport() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles windows messages sent to the notify tray icon +//----------------------------------------------------------------------------- +static void staticNotifyIconProc(HWND hwnd, WPARAM wparam, LPARAM lparam) +{ + switch (lparam) + { + case WM_LBUTTONDOWN: + { + // notify the window of the double click + if (g_Surface.GetNotifyPanel()) + { + g_pIVgui->PostMessage(g_pSurface->GetNotifyPanel(), new KeyValues("NotifyIconMsg", "msg", "WM_LBUTTONDOWN"), NULL); + } + break; + } + + case WM_LBUTTONDBLCLK: + { + //HACK: always bring us to front if the user double clicks the icon + ::SetForegroundWindow(hwnd); + + // notify the window of the double click + if (g_pSurface->GetNotifyPanel()) + { + g_pIVgui->PostMessage(g_pSurface->GetNotifyPanel(), new KeyValues("NotifyIconMsg", "msg", "LBUTTONDBLCLK"), NULL); + } + break; + } + + case WM_RBUTTONUP: // win95/98/NT + case WM_CONTEXTMENU: // win2k/ME + { + // display context menu + if (g_pSurface->GetNotifyPanel()) + { + g_pIVgui->PostMessage(g_pSurface->GetNotifyPanel(), new KeyValues("NotifyIconMsg", "msg", "CONTEXTMENU"), NULL); + } + break; + } + default: + break; + } +} + +static KeyCode g_iPreviousKeyCode = KEY_NONE; + +static void PostCursorMoved( HWND hwnd, LPARAM lparam ) +{ + POINT pt; + pt.x = (short)LOWORD(lparam); + pt.y = (short)HIWORD(lparam); + ::ClientToScreen( hwnd, &pt); + g_pInput->InternalCursorMoved(pt.x, pt.y); + +} + +static LRESULT CALLBACK staticProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) +{ + static UINT s_uTaskbarRestart; + + VPANEL panel = NULL; + IClientPanel *client = NULL; + + if (staticSurfaceAvailable) + { + panel = g_pIVgui->HandleToPanel(::GetWindowLong(hwnd, GWL_USERDATA)); + + if (panel) + { + client = ((VPanel *)panel)->Client(); + } + } + + // special case msg handle + if (msg == staticShutdownMsg) + { + // we're being notified that we have to Shutdown + g_pIVgui->ShutdownMessage(lparam); + return ::DefWindowProc(hwnd,msg,wparam,lparam); + } + + if (msg == WM_ENDSESSION && wparam == TRUE) + { + // system is being shutdown + // after this message is processed, the app will be terminated + // so all necessary shutdown functions need to occur now + if (g_pSurface->GetEmbeddedPanel()) + { + g_pIPanel->SendMessage(g_pSurface->GetEmbeddedPanel(), new KeyValues("WindowsEndSession"), NULL); + } + return 0; + } + + if (!panel) + { + return ::DefWindowProc(hwnd,msg,wparam,lparam); + } + + bool sendToDefWindowProc = true; + // md: temporarily disabled until the infinite recursion gets fixed. + + if ( ImmIsUIMessage( NULL, msg, wparam, lparam ) ) + { + sendToDefWindowProc = false; + } + + switch (msg) + { + case MS_WM_XBUTTONDOWN: + { + PostCursorMoved( hwnd, lparam ); + MouseCode code = ( HIWORD( wparam ) == 1 ) ? MOUSE_4 : MOUSE_5; + g_pInput->SetMouseCodeState( code, BUTTON_PRESSED ); + g_pInput->InternalMousePressed( code ); + break; + } + case MS_WM_XBUTTONUP: + { + PostCursorMoved( hwnd, lparam ); + MouseCode code = ( HIWORD( wparam ) == 1 ) ? MOUSE_4 : MOUSE_5; + g_pInput->SetMouseCodeState( code, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased( code ); + break; + } + + case MS_WM_XBUTTONDBLCLK: + { + PostCursorMoved( hwnd, lparam ); + MouseCode code = ( HIWORD( wparam ) == 1 ) ? MOUSE_4 : MOUSE_5; + g_pInput->SetMouseCodeState( code, BUTTON_DOUBLECLICKED ); + g_pInput->InternalMouseDoublePressed( code ); + break; + } + + case WM_CREATE: + { + s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); + break; + } + case WM_CLOSE: + { + // tell the panel to close + g_pIVgui->PostMessage(panel, new KeyValues("Close"), NULL); + + // don't Run default message pump, as that destroys the window + return 0; + } + case WM_MY_TRAY_NOTIFICATION: + { + staticNotifyIconProc(hwnd, wparam, lparam); + break; + } + case WM_SETFOCUS: + { + g_Surface.setFocus(panel); + //g_pIVgui->DPrintf("Set Focus %s %p\n", client->GetName(), panel); + break; + } + case WM_KILLFOCUS: + { + g_Surface.setFocus(NULL); + break; + } + case WM_APP: + { + g_Surface.setFocus(NULL); + break; + } + case WM_SETCURSOR: + { +//!! SetCursor(staticCurrentCursor); + break; + } + case WM_MOUSEMOVE: + { + POINT pt; + pt.x = (short)LOWORD(lparam); + pt.y = (short)HIWORD(lparam); + + + // This code catches the case when a WM_LBUTTONUP is lost + + bool bLMButtonDown = wparam & MK_LBUTTON; + bool bRMButtonDown = wparam & MK_RBUTTON; + bool bMMButtonDown = wparam & MK_MBUTTON; + if ( !bLMButtonDown && g_pInput->IsMouseDown( MOUSE_LEFT ) ) + { + g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased(MOUSE_LEFT); + } + if ( !bRMButtonDown && g_pInput->IsMouseDown( MOUSE_RIGHT ) ) + { + g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased(MOUSE_LEFT); + } + if ( !bMMButtonDown && g_pInput->IsMouseDown( MOUSE_MIDDLE ) ) + { + g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased(MOUSE_MIDDLE); + } + + ::ClientToScreen((HWND)hwnd, &pt); + g_pInput->InternalCursorMoved(pt.x, pt.y); + break; + } + case WM_LBUTTONDOWN: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_PRESSED ); + g_pInput->InternalMousePressed(MOUSE_LEFT); + break; + } + case WM_RBUTTONDOWN: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_RIGHT, BUTTON_PRESSED ); + g_pInput->InternalMousePressed(MOUSE_RIGHT); + break; + } + case WM_MBUTTONDOWN: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_PRESSED ); + g_pInput->InternalMousePressed(MOUSE_MIDDLE); + break; + } + case WM_LBUTTONDBLCLK: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_DOUBLECLICKED ); + g_pInput->InternalMouseDoublePressed( MOUSE_LEFT ); + break; + } + case WM_RBUTTONDBLCLK: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_RIGHT, BUTTON_DOUBLECLICKED ); + g_pInput->InternalMouseDoublePressed( MOUSE_RIGHT ); + break; + } + case WM_MBUTTONDBLCLK: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_DOUBLECLICKED ); + g_pInput->InternalMouseDoublePressed( MOUSE_MIDDLE ); + break; + } + case WM_LBUTTONUP: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased( MOUSE_LEFT ); + break; + } + case WM_RBUTTONUP: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_RIGHT, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased( MOUSE_RIGHT ); + break; + } + case WM_MBUTTONUP: + { + PostCursorMoved( hwnd, lparam ); + g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_RELEASED ); + g_pInput->InternalMouseReleased( MOUSE_MIDDLE ); + break; + } + case WM_MOUSEWHEEL: + { + g_pInput->InternalMouseWheeled(((short)HIWORD(wparam))/WHEEL_DELTA); + break; + } + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + int code = wparam; + g_iPreviousKeyCode = KeyCode_VirtualKeyToVGUI( code ); + bool bRepeating = ( lparam & ( 1<<30 ) ) != 0; + if ( !bRepeating ) + { + g_pInput->SetKeyCodeState( g_iPreviousKeyCode, BUTTON_PRESSED ); + g_pInput->InternalKeyCodePressed( g_iPreviousKeyCode ); + } + g_pInput->InternalKeyCodeTyped( g_iPreviousKeyCode ); + + // Deal with toggles + if ( !bRepeating ) + { + if ( code == VK_CAPITAL || code == VK_SCROLL || code == VK_NUMLOCK ) + { + ButtonCode_t toggleCode; + switch( code ) + { + default: case VK_CAPITAL: toggleCode = KEY_CAPSLOCKTOGGLE; break; + case VK_SCROLL: toggleCode = KEY_SCROLLLOCKTOGGLE; break; + case VK_NUMLOCK: toggleCode = KEY_NUMLOCKTOGGLE; break; + }; + + SHORT wState = GetKeyState( code ); + bool bToggleState = ( wState & 0x1 ) != 0; + if ( bToggleState ) + { + g_pInput->SetKeyCodeState( toggleCode, BUTTON_PRESSED ); + g_pInput->InternalKeyCodePressed( toggleCode ); + } + else + { + g_pInput->SetKeyCodeState( toggleCode, BUTTON_RELEASED ); + g_pInput->InternalKeyCodeReleased( toggleCode ); + } + } + } + + break; + } + case WM_SYSCHAR: + case WM_CHAR: + { + int unichar = wparam; + g_pInput->InternalKeyTyped(unichar); + break; + } + case WM_KEYUP: + case WM_SYSKEYUP: + { + KeyCode code = KeyCode_VirtualKeyToVGUI( wparam ); + g_pInput->SetKeyCodeState( code, BUTTON_RELEASED ); + g_pInput->InternalKeyCodeReleased( code ); + break; + } + case WM_ERASEBKGND: + { + //since the vgui Invalidate call does not erase the background + //this will only be called when windows itselfs wants a Repaint. + //this is the desired behavior because this call will for the + //surface and all its children to end up being repainted, which + //is what you want when windows wants you to Repaint the surface, + //but not what you want when say a control wants to be painted + //VPanel::Repaint will always Invalidate the surface so it will + //get a WM_PAINT, but that does not necessarily mean you want + //the whole surface painted + //simply this means.. this call only happens when windows wants the Repaint + //and WM_PAINT gets called just after this to do the real painting + + client->Repaint(); +// vgui::g_pIVgui->DPrintf( "WM_ERASEBKGND(%X)\n", panel ); + break; + } + case WM_PAINT: + { + //surface was by repainted vgui or by windows itself, do the repainting all repainting + //will goes through here and nowhere else + + // post Paint messages to the que +// panel->SolveTraverse(); + + // post a message to Paint the window +// vgui::g_pIVgui->DPrintf( "WM_PAINT(%X)\n", panel ); +// g_pInput->PostMessage( panel, new KeyValues("Paint"), NULL ); + + // this relies on platTick() being called BEFORE dispatchMessages(), so that painting occurs + + // on the same frame that the WM_PAINT message is received +// double startTime, solveTime, paintTime; + // OLD CODE + + // check that the panel is visible all the way to the root + + bool IsVisible=g_pIPanel->IsVisible(panel); + VPANEL p= g_pIPanel->GetParent(panel); + // drill down the heirachy checking that everything is visible + while(p && IsVisible) + { + if( g_pIPanel->IsVisible(p)==false) + { + IsVisible=false; + break; + } + p=g_pIPanel->GetParent(p); + } + + if( IsVisible ) + { + PAINTSTRUCT ps; + ::BeginPaint(hwnd,&ps); + // startTime = system()->GetCurrentTime(); + g_Surface.SolveTraverse(panel, false); + // solveTime = system()->GetCurrentTime(); + g_Surface.PaintTraverse(panel); + // paintTime = system()->GetCurrentTime(); + ::EndPaint(hwnd,&ps); + } + +// debug timing code +// ivgui()->DPrintf2("Paint timings (%.3f %.3f)\n", (float)(solveTime - startTime), (float)(paintTime - solveTime)); + + /* char dbtxt[200]; + Q_snprintf(dbtxt,200,"paint:%p -- %p\n",hwnd,g_Surface.GetHTMLWindow(0)->GetIEHWND()); + OutputDebugString( dbtxt ); + */ + //clear the update rectangle so it does not get another Repaint + ::ValidateRect(hwnd, NULL); + + break; + } + default: + { + if (msg == s_uTaskbarRestart) + { + // notify window to re-add taskbar icons + g_pIVgui->PostMessage(panel, new KeyValues("TaskbarRestart"), NULL); + } + + break; + } + } + + if ( sendToDefWindowProc ) + { + return DefWindowProc(hwnd,msg,wparam,lparam); + } + return TRUE; +} diff --git a/vgui2/src/System.cpp b/vgui2/src/System.cpp new file mode 100644 index 0000000..381396f --- /dev/null +++ b/vgui2/src/System.cpp @@ -0,0 +1,1151 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#if !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#define OEMRESOURCE +#include <windows.h> +#include <shellapi.h> +#include <shlwapi.h> +#include <shlobj.h> +#endif +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <sys/stat.h> + + +#ifdef ShellExecute +#undef ShellExecute +#endif + +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#ifdef GetTickCount +#undef GetTickCount +#endif + +#include <vgui/VGUI.h> +#include <vgui/ISystem.h> +#include <KeyValues.h> +#include <vgui/IInputInternal.h> +#include <vgui/ISurface.h> +#include "tier0/vcrmode.h" +#include "filesystem.h" + +#include "vgui_internal.h" +#include "filesystem_helpers.h" +#include "vgui_key_translation.h" +#include "filesystem.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +#define PROTECTED_THINGS_DISABLE +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + + + +#ifndef _X360 + + +////////////////////////////////////////////////////////////////////////// +// +// Service Windows routines +// +////////////////////////////////////////////////////////////////////////// + +static BOOL CALLBACK GetMainApplicationWindowHWND_EnumProc( HWND hWnd, LPARAM lParam ) +{ + // Return TRUE to continue enumeration or FALSE to stop + + // Given window thread/process id + DWORD dwProcessId, dwThreadId; + dwThreadId = GetWindowThreadProcessId( hWnd, &dwProcessId ); + + // Our thread/process id + DWORD dwOurProcessId; + dwOurProcessId = GetCurrentProcessId(); + + // Foreign process + if ( dwOurProcessId != dwProcessId ) + return TRUE; + // Service window + if ( !IsWindowVisible( hWnd ) || !IsWindowEnabled( hWnd ) ) + return TRUE; + + // Assume that we found it + *( HWND * )lParam = hWnd; + return FALSE; // stop enumeration +} + +static HWND GetMainApplicationWindowHWND() +{ + HWND hWnd = NULL; + + // + // Pass 1: calling on a GUI thread + // + DWORD dwThreadId = GetCurrentThreadId(); + + GUITHREADINFO gti; + memset( >i, 0, sizeof( gti ) ); + gti.cbSize = sizeof( gti ); + GetGUIThreadInfo( dwThreadId, >i ); + + hWnd = gti.hwndActive; + for ( HWND hParent = hWnd ? GetParent( hWnd ) : hWnd; + hParent; hWnd = hParent, hParent = GetParent( hWnd ) ) + continue; + + if ( hWnd ) + return hWnd; + + // + // Pass 2: non-GUI thread requiring the main winow + // + EnumWindows( GetMainApplicationWindowHWND_EnumProc, ( LPARAM ) &hWnd ); + if ( hWnd ) + return hWnd; + + // Failed to find the window by all means... + return NULL; +} + + +#endif // #ifndef _X360 + + + +////////////////////////////////////////////////////////////////////////// +// +// ISystem implementation +// +////////////////////////////////////////////////////////////////////////// + + +using namespace vgui; + +SHORT System_GetKeyState( int virtualKeyCode ) +{ +#ifndef _X360 + return VCRHook_GetKeyState(virtualKeyCode); +#else + return 0; +#endif +} + +class CSystem : public ISystem +{ +public: + CSystem(); + ~CSystem(); + + virtual void Shutdown(); + virtual void RunFrame(); + + virtual long GetTimeMillis(); + + // returns the time at the start of the frame + virtual double GetFrameTime(); + + // returns the current time + virtual double GetCurrentTime(); + + virtual void ShellExecute(const char *command, const char *file); + + virtual int GetClipboardTextCount(); + virtual void SetClipboardText(const char *text, int textLen); + virtual void SetClipboardText(const wchar_t *text, int textLen); + virtual int GetClipboardText(int offset, char *buf, int bufLen); + virtual int GetClipboardText(int offset, wchar_t *buf, int bufLen); + + virtual void SetClipboardImage( void *pWnd, int x1, int y1, int x2, int y2 ); + + virtual bool SetRegistryString(const char *key, const char *value); + virtual bool GetRegistryString(const char *key, char *value, int valueLen); + virtual bool SetRegistryInteger(const char *key, int value); + virtual bool GetRegistryInteger(const char *key, int &value); + virtual bool DeleteRegistryKey(const char *keyName); + + virtual bool SetWatchForComputerUse(bool state); + virtual double GetTimeSinceLastUse(); + virtual int GetAvailableDrives(char *buf, int bufLen); + virtual double GetFreeDiskSpace(const char *path); + + virtual KeyValues *GetUserConfigFileData(const char *dialogName, int dialogID); + virtual void SetUserConfigFile(const char *fileName, const char *pathName); + virtual void SaveUserConfigFile(); + + virtual bool CommandLineParamExists(const char *commandName); + virtual bool GetCommandLineParamValue(const char *paramName, char *value, int valueBufferSize); + virtual const char *GetFullCommandLine(); + virtual bool GetCurrentTimeAndDate(int *year, int *month, int *dayOfWeek, int *day, int *hour, int *minute, int *second); + + // shortcut (.lnk) modification functions + virtual bool CreateShortcut(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory, const char *iconFile); + virtual bool GetShortcutTarget(const char *linkFileName, char *targetPath, char *arguments, int destBufferSizes); + virtual bool ModifyShortcutTarget(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory); + + virtual KeyCode KeyCode_VirtualKeyToVGUI( int keyCode ); + virtual const char *GetDesktopFolderPath(); + virtual void ShellExecuteEx( const char *command, const char *file, const char *pParams ); +private: + // auto-away data + bool m_bStaticWatchForComputerUse; + HHOOK m_hStaticKeyboardHook; + HHOOK m_hStaticMouseHook; + double m_StaticLastComputerUseTime; + int m_iStaticMouseOldX, m_iStaticMouseOldY; + + // timer data + double m_flFrameTime; + KeyValues *m_pUserConfigData; + char m_szFileName[MAX_PATH]; + char m_szPathID[MAX_PATH]; + +}; + + +CSystem g_System; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CSystem, ISystem, VGUI_SYSTEM_INTERFACE_VERSION, g_System); + +namespace vgui +{ +vgui::ISystem *g_pSystem = &g_System; +} +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSystem::CSystem() +{ + m_bStaticWatchForComputerUse = false; + m_hStaticKeyboardHook = NULL; + m_hStaticMouseHook = NULL; + m_StaticLastComputerUseTime = 0.0; + m_iStaticMouseOldX = m_iStaticMouseOldY = -1; + m_flFrameTime = 0.0; + m_pUserConfigData = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSystem::~CSystem() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSystem::Shutdown() +{ + if (m_pUserConfigData) + { + m_pUserConfigData->deleteThis(); + m_pUserConfigData = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handles all the per frame actions +//----------------------------------------------------------------------------- +void CSystem::RunFrame() +{ + // record the current frame time + m_flFrameTime = GetCurrentTime(); + + if (m_bStaticWatchForComputerUse) + { + // check for mouse movement + int x, y; + g_pInput->GetCursorPos(x, y); + // allow a little slack for jittery mice, don't reset until it's moved more than fifty pixels + if (abs((x + y) - (m_iStaticMouseOldX + m_iStaticMouseOldY)) > 50) + { + m_StaticLastComputerUseTime = GetTimeMillis() / 1000.0; + m_iStaticMouseOldX = x; + m_iStaticMouseOldY = y; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the time at the start of the frame +//----------------------------------------------------------------------------- +double CSystem::GetFrameTime() +{ + return m_flFrameTime; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the current time +//----------------------------------------------------------------------------- +double CSystem::GetCurrentTime() +{ + return Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the current time in milliseconds +//----------------------------------------------------------------------------- +long CSystem::GetTimeMillis() +{ + return (long)(GetCurrentTime() * 1000); +} + +void CSystem::ShellExecute( const char *command, const char *file ) +{ +#ifndef _X360 + ::ShellExecuteA(NULL, command, file, NULL, NULL, SW_SHOWNORMAL); +#endif +} + +void CSystem::ShellExecuteEx( const char *command, const char *file, const char *pParams ) +{ +#ifndef _X360 + ::ShellExecuteA(NULL, command, file, pParams, NULL, SW_SHOWNORMAL); +#endif +} + + +void CSystem::SetClipboardImage( void *pWnd, int x1, int y1, int x2, int y2 ) +{ +#ifndef _X360 + if ( x2 <= x1 || y2 <= y1 ) + return; + + // Main app window + HWND hWnd = ( HWND ) ( pWnd ); + if ( !hWnd ) + hWnd = GetMainApplicationWindowHWND(); + if ( !hWnd ) + return; + + // Prepare the blit + HBITMAP hBmMem = NULL; + { + // Device contexts + HDC hDc = GetDC( hWnd ); + HDC hDcMem = CreateCompatibleDC( hDc ); + hBmMem = CreateCompatibleBitmap( hDc, x2 - x1, y2 - y1 ); + HBITMAP hBmOld = ( HBITMAP ) SelectObject( hDcMem, hBmMem ); + BitBlt( hDcMem, 0, 0, x2 - x1, y2 - y1, hDc, x1, y1, SRCCOPY ); + SelectObject( hDcMem, hBmOld ); + DeleteDC( hDcMem ); + ReleaseDC( hWnd, hDc ); + // hBmMem now holds the image. + } + + if ( !OpenClipboard( GetDesktopWindow() ) ) + return; + + EmptyClipboard(); + + if (hBmMem) + { + SetClipboardData(CF_BITMAP, hBmMem); + } + + CloseClipboard(); +#endif +} + +void CSystem::SetClipboardText(const char *text, int textLen) +{ +#ifndef _X360 + if (!text) + return; + + if (textLen <= 0) + return; + + if (!OpenClipboard(GetDesktopWindow() )) + return; + + EmptyClipboard(); + + HANDLE hmem = GlobalAlloc(GMEM_MOVEABLE, textLen + 1); + if (hmem) + { + void *ptr = GlobalLock(hmem); + if (ptr != null) + { + memset(ptr, 0, textLen + 1); + memcpy(ptr, text, textLen); + GlobalUnlock(hmem); + + SetClipboardData(CF_TEXT, hmem); + } + } + + CloseClipboard(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Puts unicode text into the clipboard +//----------------------------------------------------------------------------- +void CSystem::SetClipboardText(const wchar_t *text, int textLen) +{ +#ifndef _X360 + if (!text) + return; + + if (textLen <= 0) + return; + + BOOL cb = OpenClipboard(GetDesktopWindow() ); + if (!cb) + return; + + EmptyClipboard(); + + HANDLE hmem = GlobalAlloc(GMEM_MOVEABLE, (textLen + 1) * sizeof(wchar_t)); + if (hmem) + { + void *ptr = GlobalLock(hmem); + if (ptr != null) + { + memset(ptr, 0, (textLen + 1) * sizeof(wchar_t)); + memcpy(ptr, text, textLen * sizeof(wchar_t)); + GlobalUnlock(hmem); + + SetClipboardData( CF_UNICODETEXT, hmem ); + } + } + + CloseClipboard(); +#endif +} + +int CSystem::GetClipboardTextCount() +{ +#ifndef _X360 + int count = 0; + + if ( VCRGetMode() != VCR_Playback ) + { + if (OpenClipboard(GetDesktopWindow() )) + { + HANDLE hmem = GetClipboardData(CF_TEXT); + if (hmem) + { + count = GlobalSize(hmem); + } + + CloseClipboard(); + } + } + VCRGenericValue( "clipboard", &count, sizeof( count ) ); + + return count; +#else + return 0; +#endif +} + +int CSystem::GetClipboardText(int offset, char *buf, int bufLen) +{ +#ifndef _X360 + int count = 0; + if ( buf && bufLen > 0 && VCRGetMode() != VCR_Playback ) + { + if (OpenClipboard(GetDesktopWindow())) + { + HANDLE hmem = GetClipboardData(CF_UNICODETEXT); + if (hmem) + { + int len = GlobalSize(hmem); + count = len - offset; + if (count <= 0) + { + count = 0; + } + else + { + if (bufLen < count) + { + count = bufLen; + } + void *ptr = GlobalLock(hmem); + if (ptr) + { + memcpy(buf, ((char *)ptr) + offset, count); + GlobalUnlock(hmem); + } + } + } + + CloseClipboard(); + } + } + VCRGenericValue( "cb", &count, sizeof( count ) ); + VCRGenericValue( "cb", buf, count ); + + return count; +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Retrieves unicode text from the clipboard +//----------------------------------------------------------------------------- +int CSystem::GetClipboardText(int offset, wchar_t *buf, int bufLen) +{ +#ifndef _X360 + int retVal = 0; + if ( buf && bufLen > 0 && VCRGetMode() != VCR_Playback ) + { + if (OpenClipboard( GetDesktopWindow() ) ) + { + HANDLE hmem = GetClipboardData(CF_UNICODETEXT); + if (hmem) + { + int len = GlobalSize(hmem); + int count = len - offset; + if (count > 0) + { + if (bufLen < count) + { + count = bufLen; + } + void *ptr = GlobalLock(hmem); + if (ptr) + { + memcpy(buf, ((wchar_t *)ptr) + offset, count); + retVal = count / sizeof(wchar_t); + GlobalUnlock(hmem); + } + } + } + } + + CloseClipboard(); + } + + VCRGenericValue( "cb", &retVal, sizeof( retVal ) ); + VCRGenericValue( "cb", buf, retVal*sizeof(wchar_t) ); + + return retVal; +#else + return 0; +#endif +} + +static bool staticSplitRegistryKey(const char *key, char *key0, int key0Len, char *key1, int key1Len) +{ + if(key==null) + { + return false; + } + + int len=strlen(key); + if(len<=0) + { + return false; + } + + int Start=-1; + for(int i=len-1;i>=0;i--) + { + if(key[i]=='\\') + { + break; + } + else + { + Start=i; + } + } + + if(Start==-1) + { + return false; + } + + vgui_strcpy(key0,Start+1,key); + vgui_strcpy(key1,(len-Start)+1,key+Start); + + return true; +} + +bool CSystem::SetRegistryString(const char *key, const char *value) +{ +#ifndef _X360 + HKEY hKey; + + HKEY hSlot = HKEY_CURRENT_USER; + if (!strncmp(key, "HKEY_LOCAL_MACHINE", 18)) + { + hSlot = HKEY_LOCAL_MACHINE; + key += 19; + } + else if (!strncmp(key, "HKEY_CURRENT_USER", 17)) + { + hSlot = HKEY_CURRENT_USER; + key += 18; + } + + char key0[256], key1[256]; + if (!staticSplitRegistryKey(key, key0, sizeof(key0), key1, sizeof(key1))) + { + return false; + } + + if(VCRHook_RegCreateKeyEx(hSlot,key0,null,null,REG_OPTION_NON_VOLATILE, value ? KEY_WRITE : KEY_ALL_ACCESS,null,&hKey,null)!=ERROR_SUCCESS) + { + return false; + } + + if (VCRHook_RegSetValueEx(hKey, key1, NULL, REG_SZ, (uchar*)value, strlen(value) + 1) == ERROR_SUCCESS) + { + VCRHook_RegCloseKey(hKey); + return true; + } + + VCRHook_RegCloseKey(hKey); +#endif + + return false; +} + +bool CSystem::GetRegistryString(const char *key, char *value, int valueLen) +{ +#ifndef _X360 + if (!value) + return false; + value[0] = 0; + + HKEY hKey; + + HKEY hSlot = HKEY_CURRENT_USER; + if (!strncmp(key, "HKEY_LOCAL_MACHINE", 18)) + { + hSlot = HKEY_LOCAL_MACHINE; + key += 19; + } + else if (!strncmp(key, "HKEY_CURRENT_USER", 17)) + { + hSlot = HKEY_CURRENT_USER; + key += 18; + } + + char key0[256],key1[256]; + if(!staticSplitRegistryKey(key,key0,256,key1,256)) + { + return false; + } + + if(VCRHook_RegOpenKeyEx(hSlot,key0,null,KEY_READ,&hKey)!=ERROR_SUCCESS) + { + return false; + } + + ulong len=valueLen; + if(VCRHook_RegQueryValueEx(hKey,key1,null,null,(uchar*)value,&len)==ERROR_SUCCESS) + { + VCRHook_RegCloseKey(hKey); + return true; + } + + VCRHook_RegCloseKey(hKey); +#endif + + return false; +} + +bool CSystem::SetRegistryInteger(const char *key, int value) +{ +#ifndef _X360 + HKEY hKey; + HKEY hSlot = HKEY_CURRENT_USER; + if (!strncmp(key, "HKEY_LOCAL_MACHINE", 18)) + { + hSlot = HKEY_LOCAL_MACHINE; + key += 19; + } + else if (!strncmp(key, "HKEY_CURRENT_USER", 17)) + { + hSlot = HKEY_CURRENT_USER; + key += 18; + } + + char key0[256],key1[256]; + if(!staticSplitRegistryKey(key,key0,256,key1,256)) + { + return false; + } + + if(VCRHook_RegCreateKeyEx(hSlot,key0,null,null,REG_OPTION_NON_VOLATILE,KEY_WRITE,null,&hKey,null)!=ERROR_SUCCESS) + { + return false; + } + + if(VCRHook_RegSetValueEx(hKey,key1,null,REG_DWORD,(uchar*)&value,4)==ERROR_SUCCESS) + { + VCRHook_RegCloseKey(hKey); + return true; + } + + VCRHook_RegCloseKey(hKey); +#endif + return false; +} + +bool CSystem::GetRegistryInteger(const char *key, int &value) +{ +#ifndef _X360 + HKEY hKey; + HKEY hSlot = HKEY_CURRENT_USER; + if (!strncmp(key, "HKEY_LOCAL_MACHINE", 18)) + { + hSlot = HKEY_LOCAL_MACHINE; + key += 19; + } + else if (!strncmp(key, "HKEY_CURRENT_USER", 17)) + { + hSlot = HKEY_CURRENT_USER; + key += 18; + } + + char key0[256],key1[256]; + if(!staticSplitRegistryKey(key,key0,256,key1,256)) + { + return false; + } + + if(VCRHook_RegOpenKeyEx(hSlot,key0,null,KEY_READ,&hKey)!=ERROR_SUCCESS) + { + return false; + } + + ulong len=4; + if(VCRHook_RegQueryValueEx(hKey,key1,null,null,(uchar*)&value,&len)==ERROR_SUCCESS) + { + VCRHook_RegCloseKey(hKey); + return true; + } + + VCRHook_RegCloseKey(hKey); +#endif + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: recursively deletes a registry key and all it's subkeys +//----------------------------------------------------------------------------- +bool CSystem::DeleteRegistryKey(const char *key) +{ +#ifndef _X360 + HKEY hSlot = HKEY_CURRENT_USER; + if (!strncmp(key, "HKEY_LOCAL_MACHINE", 18)) + { + hSlot = HKEY_LOCAL_MACHINE; + key += 19; + } + else if (!strncmp(key, "HKEY_CURRENT_USER", 17)) + { + hSlot = HKEY_CURRENT_USER; + key += 18; + } + + if (SHDeleteKey(hSlot, key) == ERROR_SUCCESS) + { + return true; + } +#endif + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the app watches for global computer use +//----------------------------------------------------------------------------- +bool CSystem::SetWatchForComputerUse(bool state) +{ + if (state == m_bStaticWatchForComputerUse) + return true; + + m_bStaticWatchForComputerUse = state; + + if (m_bStaticWatchForComputerUse) + { + // enable watching + } + else + { + // disable watching + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the time, in seconds, since the last computer use. +//----------------------------------------------------------------------------- +double CSystem::GetTimeSinceLastUse() +{ + if (m_bStaticWatchForComputerUse) + { + return (GetTimeMillis() / 1000.0) - m_StaticLastComputerUseTime; + } + + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the drives a user has available on their system +//----------------------------------------------------------------------------- +int CSystem::GetAvailableDrives(char *buf, int bufLen) +{ +#if defined( _X360 ) || defined ( OSX ) + return 0; +#else // Windows + return GetLogicalDriveStrings( bufLen, buf ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: returns the amount of available disk space, in bytes, on the specified path +//----------------------------------------------------------------------------- +double CSystem::GetFreeDiskSpace(const char *path) +{ + char buf[_MAX_PATH]; + strcpy(buf, path); + // strip of to first slash (to make it look like 'x:\') + char *slash = strstr(buf, "\\"); + if (slash) + { + slash[1] = 0; + } + + ULARGE_INTEGER userFreeBytes, totalBytes, totalFreeBytes; + if (::GetDiskFreeSpaceEx(buf, &userFreeBytes, &totalBytes, &totalFreeBytes)) + { + return (double)userFreeBytes.QuadPart; + } + return 0.0; +} + +//----------------------------------------------------------------------------- +// Purpose: user config +//----------------------------------------------------------------------------- +KeyValues *CSystem::GetUserConfigFileData(const char *dialogName, int dialogID) +{ + if (!m_pUserConfigData) + return NULL; + + Assert(dialogName && *dialogName); + + if (dialogID) + { + char buf[256]; + Q_snprintf(buf, sizeof(buf), "%s_%d", dialogName, dialogID); + dialogName = buf; + } + + return m_pUserConfigData->FindKey(dialogName, true); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the name of the config file to save/restore from. Settings are loaded immediately. +//----------------------------------------------------------------------------- +void CSystem::SetUserConfigFile(const char *fileName, const char *pathName) +{ + if (!m_pUserConfigData) + { + m_pUserConfigData = new KeyValues("UserConfigData"); + } + + strncpy(m_szFileName, fileName, sizeof(m_szFileName) - 1); + strncpy(m_szPathID, pathName, sizeof(m_szPathID) - 1); + + // open + m_pUserConfigData->UsesEscapeSequences( true ); // VGUI may use this + m_pUserConfigData->LoadFromFile(g_pFullFileSystem, m_szFileName, m_szPathID); +} + +//----------------------------------------------------------------------------- +// Purpose: saves all the current settings to the user config file +//----------------------------------------------------------------------------- +void CSystem::SaveUserConfigFile() +{ + if (m_pUserConfigData) + { + m_pUserConfigData->SaveToFile(g_pFullFileSystem, m_szFileName, m_szPathID); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns whether or not the parameter was on the command line +//----------------------------------------------------------------------------- +bool CSystem::CommandLineParamExists(const char *paramName) +{ + const char *cmdLine = GetFullCommandLine(); + const char *loc = strstr(cmdLine, paramName); + return (loc != NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the string following a command line param +//----------------------------------------------------------------------------- +bool CSystem::GetCommandLineParamValue(const char *paramName, char *value, int valueBufferSize) +{ + const char *cmdLine = GetFullCommandLine(); + const char *loc = strstr(cmdLine, paramName); + if (!loc) + return false; + + loc += strlen(paramName); + + char token[512]; + ParseFile(loc, token, NULL); + + strncpy(value, token, valueBufferSize - 1); + value[valueBufferSize - 1] = 0; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name of the currently running exe +//----------------------------------------------------------------------------- +const char *CSystem::GetFullCommandLine() +{ + return VCRHook_GetCommandLine(); +} + + +KeyCode CSystem::KeyCode_VirtualKeyToVGUI( int keyCode ) +{ + return ::KeyCode_VirtualKeyToVGUI( keyCode ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the current local time and date +//----------------------------------------------------------------------------- +bool CSystem::GetCurrentTimeAndDate(int *year, int *month, int *dayOfWeek, int *day, int *hour, int *minute, int *second) +{ + SYSTEMTIME time; + GetLocalTime(&time); + if (year) + { + *year = time.wYear; + } + if (month) + { + *month = time.wMonth; + } + if (dayOfWeek) + { + *dayOfWeek = time.wDayOfWeek; + } + if (day) + { + *day = time.wDay; + } + if (hour) + { + *hour = time.wHour; + } + if (minute) + { + *minute = time.wMinute; + } + if (second) + { + *second = time.wSecond; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates a shortcut file +//----------------------------------------------------------------------------- +bool CSystem::CreateShortcut(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory, const char *iconFile) +{ +#ifndef _X360 + bool bSuccess = false; + char temp[MAX_PATH]; + strcpy(temp, linkFileName); + + // make sure it doesn't already exist + struct _stat statBuf; + if (_stat(linkFileName, &statBuf) != -1) + return false; + + // Create the ShellLink object + IShellLink *psl; + HRESULT hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl); + if (SUCCEEDED(hres)) + { + // Set the target information from the link object + psl->SetPath(targetPath); + psl->SetArguments(arguments); + if (workingDirectory && *workingDirectory) + { + psl->SetWorkingDirectory(workingDirectory); + } + if (iconFile && *iconFile) + { + psl->SetIconLocation(iconFile, 0); + } + + // Bind the ShellLink object to the Persistent File + IPersistFile *ppf; + hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf); + if (SUCCEEDED(hres)) + { + wchar_t wsz[MAX_PATH]; + // Get a UNICODE wide string wsz from the link path + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, temp, -1, wsz, MAX_PATH); + hres = ppf->Save(wsz, TRUE); + if (SUCCEEDED(hres)) + { + bSuccess = true; + } + ppf->Release(); + } + psl->Release(); + } + return bSuccess; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: retrieves shortcut (.lnk) information +//----------------------------------------------------------------------------- +bool CSystem::GetShortcutTarget(const char *linkFileName, char *targetPath, char *arguments, int destBufferSizes) +{ +#ifndef _X360 + char temp[MAX_PATH]; + strcpy(temp, linkFileName); + strlwr(temp); + + targetPath[0] = 0; + arguments[0] = 0; + + // Create the ShellLink object + IShellLink *psl; + HRESULT hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl); + if (SUCCEEDED(hres)) + { + IPersistFile *ppf; + // Bind the ShellLink object to the Persistent File + hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf); + if (SUCCEEDED(hres)) + { + wchar_t wsz[MAX_PATH]; + // Get a UNICODE wide string wsz from the link path + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, temp, -1, wsz, MAX_PATH); + + // Read the link into the persistent file + hres = ppf->Load(wsz, 0); + + if (SUCCEEDED(hres)) + { + //Read the target information from the link object + //UNC paths are supported (SLGP_UNCPRIORITY) + psl->GetPath(targetPath, destBufferSizes, NULL, SLGP_UNCPRIORITY); + + //Read the arguments from the link object + psl->GetArguments(arguments, destBufferSizes); + } + ppf->Release(); + } + psl->Release(); + } + return (targetPath[0] != 0); +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: sets shortcut (.lnk) information +//----------------------------------------------------------------------------- +bool CSystem::ModifyShortcutTarget(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory) +{ +#ifndef _X360 + bool bSuccess = false; + char temp[MAX_PATH]; + strcpy(temp, linkFileName); + strlwr(temp); + + // Create the ShellLink object + IShellLink *psl; + HRESULT hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl); + if (SUCCEEDED(hres)) + { + IPersistFile *ppf; + // Bind the ShellLink object to the Persistent File + hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf); + if (SUCCEEDED(hres)) + { + wchar_t wsz[MAX_PATH]; + // Get a UNICODE wide string wsz from the link path + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, temp, -1, wsz, MAX_PATH); + + // Read the link into the persistent file + hres = ppf->Load(wsz, 0); + + if (SUCCEEDED(hres)) + { + // Set the target information from the link object + psl->SetPath(targetPath); + psl->SetArguments(arguments); + psl->SetWorkingDirectory(workingDirectory); + bSuccess = true; + ppf->Save(wsz, TRUE); + } + ppf->Release(); + } + psl->Release(); + } + return bSuccess; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: returns the full path of the current user's desktop folder +//----------------------------------------------------------------------------- +const char *CSystem::GetDesktopFolderPath() +{ +#ifndef _X360 + static char folderPath[MAX_PATH]; + folderPath[0] = 0; + + // try the custom regkey + if ( GetRegistryString( "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\Desktop", folderPath, sizeof(folderPath) ) + && strlen(folderPath) > 6 ) + { + return folderPath; + } + + + // try the default + if ( ::SHGetSpecialFolderPath( NULL, folderPath, CSIDL_DESKTOP, false ) + && strlen(folderPath) > 6 ) + { + return folderPath; + } +#endif + return NULL; +} diff --git a/vgui2/src/VGUI_Border.h b/vgui2/src/VGUI_Border.h new file mode 100644 index 0000000..9c2f2e5 --- /dev/null +++ b/vgui2/src/VGUI_Border.h @@ -0,0 +1,82 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Core implementation of vgui +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VGUI_BORDER_H +#define VGUI_BORDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include <vgui/IBorder.h> +#include <vgui/IScheme.h> +#include <Color.h> + +class KeyValues; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Interface to panel borders +// Borders have a close relationship with panels +//----------------------------------------------------------------------------- +class Border : public IBorder +{ +public: + Border(); + ~Border(); + + virtual void Paint(VPANEL panel); + virtual void Paint(int x0, int y0, int x1, int y1); + virtual void Paint(int x0, int y0, int x1, int y1, int breakSide, int breakStart, int breakStop); + virtual void SetInset(int left, int top, int right, int bottom); + virtual void GetInset(int &left, int &top, int &right, int &bottom); + + virtual void ApplySchemeSettings(IScheme *pScheme, KeyValues *inResourceData); + + virtual const char *GetName(); + virtual void SetName(const char *name); + virtual backgroundtype_e GetBackgroundType(); + + virtual bool PaintFirst( void ) { return false; } + +protected: + int _inset[4]; + +private: + // protected copy constructor to prevent use + Border(Border&); + + void ParseSideSettings(int side_index, KeyValues *inResourceData, IScheme *pScheme); + + char *_name; + + // border drawing description + struct line_t + { + Color col; + int startOffset; + int endOffset; + }; + + struct side_t + { + int count; + line_t *lines; + }; + + side_t _sides[4]; // left, top, right, bottom + backgroundtype_e m_eBackgroundType; + + friend class VPanel; +}; + +} // namespace vgui + +#endif // VGUI_BORDER_H diff --git a/vgui2/src/VPanel.cpp b/vgui2/src/VPanel.cpp new file mode 100644 index 0000000..dcdfb81 --- /dev/null +++ b/vgui2/src/VPanel.cpp @@ -0,0 +1,782 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <stdio.h> + +#include <vgui/IPanel.h> +#include <vgui/IClientPanel.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/Cursor.h> + +#include "vgui_internal.h" +#include "VPanel.h" + +#include "tier0/minidump.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +// Lame copy from Panel +enum PinCorner_e +{ + PIN_TOPLEFT = 0, + PIN_TOPRIGHT, + PIN_BOTTOMLEFT, + PIN_BOTTOMRIGHT, + + // For sibling pinning + PIN_CENTER_TOP, + PIN_CENTER_RIGHT, + PIN_CENTER_BOTTOM, + PIN_CENTER_LEFT, + + NUM_PIN_POINTS, +}; + +float PinDeltas[NUM_PIN_POINTS][2] = +{ + { 0, 0 }, // PIN_TOPLEFT, + { 1, 0 }, // PIN_TOPRIGHT, + { 0, 1 }, // PIN_BOTTOMLEFT, + { 1, 1 }, // PIN_BOTTOMRIGHT, + { 0.5, 0 }, // PIN_CENTER_TOP, + { 1, 0.5 }, // PIN_CENTER_RIGHT, + { 0.5, 1 }, // PIN_CENTER_BOTTOM, + { 0, 0.5 }, // PIN_CENTER_LEFT, +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPanel::VPanel() +{ + _pos[0] = _pos[1] = 0; + _absPos[0] = _absPos[1] = 0; + _size[0] = _size[1] = 0; + + _minimumSize[0] = 0; + _minimumSize[1] = 0; + + _zpos = 0; + + _inset[0] = _inset[1] = _inset[2] = _inset[3] = 0; + _clipRect[0] = _clipRect[1] = _clipRect[2] = _clipRect[3] = 0; + + _visible = true; + _enabled = true; + _clientPanel = NULL; + _parent = NULL; + _plat = NULL; + _popup = false; + _isTopmostPopup = false; + _hPanel = INVALID_PANEL; + + _mouseInput = true; // by default you want mouse and kb input to this panel + _kbInput = true; + + _pinsibling = NULL; + _pinsibling_my_corner = PIN_TOPLEFT; + _pinsibling_their_corner = PIN_TOPLEFT; + + m_nThinkTraverseLevel = 0; + _clientPanelHandle = vgui::INVALID_PANEL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPanel::~VPanel() +{ + // Someone just deleted their parent Panel while it was being used in InternalSolveTraverse(). + // This will cause a difficult to debug crash, so we spew out the panel name here in hopes + // it will help track down the offender. + if ( m_nThinkTraverseLevel != 0 ) + { + Warning( "Deleting in-use vpanel: %s/%s %p.\n", GetName(), GetClassName(), this ); +#ifdef STAGING_ONLY + DebuggerBreak(); +#endif + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::TraverseLevel( int val ) +{ + // Bump up our traverse level. + m_nThinkTraverseLevel += m_nThinkTraverseLevel; + + // Bump up our client panel traverse level. + if ( Client() ) + { + VPANEL vp = g_pVGui->HandleToPanel( _clientPanelHandle ); + if ( vp == vgui::INVALID_PANEL ) + { + // This is really bad - we have a Client() pointer that is invalid. + Warning( "Panel '%s/%s' has invalid client: %p.\n", GetName(), GetClassName(), Client() ); +#ifdef STAGING_ONLY + DebuggerBreak(); +#endif + } + + if ( Client()->GetVPanel() ) + { + VPanel *vpanel = (VPanel *)Client()->GetVPanel(); + vpanel->m_nThinkTraverseLevel += vpanel->m_nThinkTraverseLevel; + } + } + + // This doesn't work. It appears we add all kinds of children to various panels in the + // InternalThinkTraverse functions, and that means the refcount is 0 when added, and + // then drops to -1 when we decrement the traverse level. +#if 0 + // Bump up our children traverse levels. + CUtlVector< VPanel * > &children = GetChildren(); + for ( int i = 0; i < children.Count(); ++i ) + { + VPanel *child = children[ i ]; + if ( child ) + child->m_nThinkTraverseLevel = Max( child->m_nThinkTraverseLevel + val, 0 ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::Init(IClientPanel *attachedClientPanel) +{ + _clientPanel = attachedClientPanel; + _clientPanelHandle = g_pVGui->PanelToHandle( attachedClientPanel ? attachedClientPanel->GetVPanel() : 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::Solve() +{ + short basePos[2]; + basePos[0] = _pos[0]; + basePos[1] = _pos[1]; + + int baseSize[2]; + GetSize( baseSize[0], baseSize[1] ); + + VPanel *parent = GetParent(); + if (IsPopup()) + { + // if we're a popup, draw at the highest level + parent = (VPanel *)g_pSurface->GetEmbeddedPanel(); + } + + int pabs[2]; + if ( parent ) + { + parent->GetAbsPos(pabs[0], pabs[1]); + } + + if ( _pinsibling ) + { + _pinsibling->Solve(); + + int sibPos[2]; + int sibSize[2]; + _pinsibling->GetInternalAbsPos( sibPos[0], sibPos[1] ); + _pinsibling->GetSize( sibSize[0], sibSize[1] ); + + for ( int i = 0; i < 2; i++ ) + { + if ( parent ) + { + sibPos[i] -= pabs[i]; + } + + // Determine which direction positive values move in. For center pins, we use screen relative signs. + int iSign = 1; + if ( i == 0 && (_pinsibling_their_corner == PIN_CENTER_LEFT || _pinsibling_their_corner == PIN_TOPLEFT || _pinsibling_their_corner == PIN_BOTTOMLEFT) ) + { + iSign = -1; + } + else if ( i == 1 && (_pinsibling_their_corner == PIN_CENTER_TOP || _pinsibling_their_corner == PIN_TOPLEFT || _pinsibling_their_corner == PIN_TOPRIGHT) ) + { + iSign = -1; + } + + int iPos = sibPos[i] + (sibSize[i] * PinDeltas[_pinsibling_their_corner][i]); + iPos -= (baseSize[i] * PinDeltas[_pinsibling_my_corner][i]); + iPos += basePos[i] * iSign; + + basePos[i] = iPos; + } + } + + int absX = basePos[0]; + int absY = basePos[1]; + _absPos[0] = basePos[0]; + _absPos[1] = basePos[1]; + + // put into parent space + int pinset[4] = {0, 0, 0, 0}; + if ( parent ) + { + parent->GetInset( pinset[0], pinset[1], pinset[2], pinset[3] ); + + absX += pabs[0] + pinset[0]; + absY += pabs[1] + pinset[1]; + + _absPos[0] = clamp( absX, -32767, 32767 ); + _absPos[1] = clamp( absY, -32767, 32767 ); + } + + // set initial bounds + _clipRect[0] = _absPos[0]; + _clipRect[1] = _absPos[1]; + + int absX2 = absX + baseSize[0]; + int absY2 = absY + baseSize[1]; + _clipRect[2] = clamp( absX2, -32767, 32767 ); + _clipRect[3] = clamp( absY2, -32767, 32767 ); + + // clip to parent, if we're not a popup + if ( parent && !IsPopup() ) + { + int pclip[4]; + parent->GetClipRect(pclip[0], pclip[1], pclip[2], pclip[3]); + + if (_clipRect[0] < pclip[0]) + { + _clipRect[0] = pclip[0]; + } + + if (_clipRect[1] < pclip[1]) + { + _clipRect[1] = pclip[1]; + } + + if(_clipRect[2] > pclip[2]) + { + _clipRect[2] = pclip[2] - pinset[2]; + } + + if(_clipRect[3] > pclip[3]) + { + _clipRect[3] = pclip[3] - pinset[3]; + } + + if ( _clipRect[0] > _clipRect[2] ) + { + _clipRect[2] = _clipRect[0]; + } + + if ( _clipRect[1] > _clipRect[3] ) + { + _clipRect[3] = _clipRect[1]; + } + } + + Assert( _clipRect[0] <= _clipRect[2] ); + Assert( _clipRect[1] <= _clipRect[3] ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetPos(int x, int y) +{ + _pos[0] = x; + _pos[1] = y; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetPos(int &x, int &y) +{ + x = _pos[0]; + y = _pos[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetSize(int wide,int tall) +{ + if (wide<_minimumSize[0]) + { + wide=_minimumSize[0]; + } + if (tall<_minimumSize[1]) + { + tall=_minimumSize[1]; + } + + if (_size[0] == wide && _size[1] == tall) + return; + + _size[0]=wide; + _size[1]=tall; + + Client()->OnSizeChanged(wide, tall); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetSize(int& wide,int& tall) +{ + wide=_size[0]; + tall=_size[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetMinimumSize(int wide,int tall) +{ + _minimumSize[0]=wide; + _minimumSize[1]=tall; + + // check if we're currently smaller than the new minimum size + int currentWidth = _size[0]; + if (currentWidth < wide) + { + currentWidth = wide; + } + int currentHeight = _size[1]; + if (currentHeight < tall) + { + currentHeight = tall; + } + + // resize to new minimum size if necessary + if (currentWidth != _size[0] || currentHeight != _size[1]) + { + SetSize(currentWidth, currentHeight); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetMinimumSize(int &wide, int &tall) +{ + wide = _minimumSize[0]; + tall = _minimumSize[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetVisible(bool state) +{ + if (_visible == state) + return; + + // need to tell the surface (in case special window processing needs to occur) + g_pSurface->SetPanelVisible((VPANEL)this, state); + + _visible = state; + + if( IsPopup() ) + { + vgui::g_pSurface->CalculateMouseVisible(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetEnabled(bool state) +{ + _enabled = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool VPanel::IsVisible() +{ + return _visible; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool VPanel::IsEnabled() +{ + return _enabled; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetAbsPos(int &x, int &y) +{ + x = _absPos[0]; + y = _absPos[1]; + + g_pSurface->OffsetAbsPos( x, y ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetInternalAbsPos(int &x, int &y) +{ + x = _absPos[0]; + y = _absPos[1]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetClipRect(int &x0, int &y0, int &x1, int &y1) +{ + x0 = _clipRect[0]; + y0 = _clipRect[1]; + x1 = _clipRect[2]; + y1 = _clipRect[3]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetInset(int left, int top, int right, int bottom) +{ + _inset[0] = left; + _inset[1] = top; + _inset[2] = right; + _inset[3] = bottom; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::GetInset(int &left, int &top, int &right, int &bottom) +{ + left = _inset[0]; + top = _inset[1]; + right = _inset[2]; + bottom = _inset[3]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void VPanel::SetParent(VPanel *newParent) +{ + if (this == newParent) + return; + + if (_parent == newParent) + return; + + if (_parent != NULL) + { + _parent->_childDar.RemoveElement(this); + _parent = null; + } + + if (newParent != NULL) + { + _parent = newParent; + _parent->_childDar.PutElement(this); + SetZPos(_zpos); // re-sort parent's panel order if necessary + if (_parent->Client()) + { + _parent->Client()->OnChildAdded((VPANEL)this); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int VPanel::GetChildCount() +{ + return _childDar.GetCount(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPanel *VPanel::GetChild(int index) +{ + return _childDar[index]; +} + +CUtlVector< VPanel *> &VPanel::GetChildren() +{ + return _childDar; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPanel *VPanel::GetParent() +{ + return _parent; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the Z position of a panel and reorders it appropriately +//----------------------------------------------------------------------------- +void VPanel::SetZPos(int z) +{ + _zpos = z; + + if (_parent) + { + // find the child in the list + int childCount = _parent->GetChildCount(); + int i; + for (i = 0; i < childCount; i++) + { + if (_parent->GetChild(i) == this) + break; + } + + if (i == childCount) + return; + + while (1) + { + VPanel *prevChild = NULL, *nextChild = NULL; + + if ( i > 0 ) + { + prevChild = _parent->GetChild( i - 1 ); + } + if ( i <(childCount - 1) ) + { + nextChild = _parent->GetChild( i + 1 ); + } + + // check either side of the child to see if it should move + if ( i > 0 && prevChild && ( prevChild->_zpos > _zpos ) ) + { + // swap with the lower + _parent->_childDar.SetElementAt(prevChild, i); + _parent->_childDar.SetElementAt(this, i - 1); + i--; + } + else if (i < (childCount - 1) && nextChild && ( nextChild->_zpos < _zpos ) ) + { + // swap with the higher + _parent->_childDar.SetElementAt(nextChild, i); + _parent->_childDar.SetElementAt(this, i + 1); + i++; + } + else + { + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the z position of this panel +//----------------------------------------------------------------------------- +int VPanel::GetZPos() +{ + return _zpos; +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the panel to the front of the z-order +//----------------------------------------------------------------------------- +void VPanel::MoveToFront(void) +{ + g_pSurface->MovePopupToFront((VPANEL)this); + + if (_parent) + { + // move this panel to the end of it's parents list + _parent->_childDar.MoveElementToEnd(this); + + // Validate the Z order + int i = _parent->_childDar.GetCount() - 2; + while (i >= 0) + { + if (_parent->_childDar[i]->_zpos > _zpos) + { + // we can't be in front of this; swap positions + _parent->_childDar.SetElementAt(_parent->_childDar[i], i + 1); + _parent->_childDar.SetElementAt(this, i); + + // check the next value + i--; + } + else + { + // order valid + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Moves the panel to the back of the z-order +//----------------------------------------------------------------------------- +void VPanel::MoveToBack() +{ + if (_parent) + { + // move this panel to the end of it's parents list + _parent->_childDar.RemoveElement(this); + _parent->_childDar.InsertElementAt(this, 0); + + // Validate the Z order + int i = 1; + while (i < _parent->_childDar.GetCount()) + { + if (_parent->_childDar[i]->_zpos < _zpos) + { + // we can't be behind this; swap positions + _parent->_childDar.SetElementAt(_parent->_childDar[i], i - 1); + _parent->_childDar.SetElementAt(this, i); + + // check the next value + i++; + } + else + { + // order valid + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Iterates up the hierarchy looking to see if a panel has the specified ancestor +//----------------------------------------------------------------------------- +bool VPanel::HasParent(VPanel *potentialParent) +{ + if (this == potentialParent) + return true; + + if (_parent) + { + return _parent->HasParent(potentialParent); + } + + return false; +} + +SurfacePlat *VPanel::Plat() +{ + return _plat; +} + +void VPanel::SetPlat(SurfacePlat *Plat) +{ + _plat = Plat; +} + +bool VPanel::IsPopup() +{ + return _popup; +} + +void VPanel::SetPopup(bool state) +{ + _popup = state; +} + +bool VPanel::IsTopmostPopup() const +{ + return _isTopmostPopup; +} + +void VPanel::SetTopmostPopup( bool bEnable ) +{ + _isTopmostPopup = bEnable; +} + +bool VPanel::IsFullyVisible() +{ + // recursively check to see if the panel and all it's parents are visible + VPanel *panel = this; + while (panel) + { + if (!panel->_visible) + { + return false; + } + + panel = panel->_parent; + } + + // we're visible all the way up the hierarchy + return true; +} + +const char *VPanel::GetName() +{ + return Client()->GetName(); +} + +const char *VPanel::GetClassName() +{ + return Client()->GetClassName(); +} + +HScheme VPanel::GetScheme() +{ + return Client()->GetScheme(); +} + + +void VPanel::SendMessage(KeyValues *params, VPANEL ifrompanel) +{ + Client()->OnMessage(params, ifrompanel); +} + + +void VPanel::SetKeyBoardInputEnabled(bool state) +{ + _kbInput = state; +} + +void VPanel::SetMouseInputEnabled(bool state) +{ + _mouseInput = state; +} + +bool VPanel::IsKeyBoardInputEnabled() +{ + return _kbInput; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool VPanel::IsMouseInputEnabled() +{ + return _mouseInput; +} + +//----------------------------------------------------------------------------- +// Purpose: sibling pins +//----------------------------------------------------------------------------- +void VPanel::SetSiblingPin(VPanel *newSibling, byte iMyCornerToPin, byte iSiblingCornerToPinTo ) +{ + _pinsibling = newSibling; + _pinsibling_my_corner = iMyCornerToPin; + _pinsibling_their_corner = iSiblingCornerToPinTo; +} diff --git a/vgui2/src/VPanel.h b/vgui2/src/VPanel.h new file mode 100644 index 0000000..754fcfd --- /dev/null +++ b/vgui2/src/VPanel.h @@ -0,0 +1,146 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VPANEL_H +#define VPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/Dar.h> +#include <vgui/IPanel.h> + +#ifdef GetClassName +#undef GetClassName +#endif + +namespace vgui +{ + + class SurfaceBase; + class IClientPanel; + struct SerialPanel_t; + + //----------------------------------------------------------------------------- + // Purpose: VGUI private implementation of panel + //----------------------------------------------------------------------------- + class VPanel + { + public: + VPanel(); + virtual ~VPanel(); + + virtual void Init(IClientPanel *attachedClientPanel); + + virtual SurfacePlat *Plat(); + virtual void SetPlat(SurfacePlat *pl); + + virtual HPanel GetHPanel() { return _hPanel; } // safe pointer handling + virtual void SetHPanel(HPanel hPanel) { _hPanel = hPanel; } + + virtual bool IsPopup(); + virtual void SetPopup(bool state); + virtual bool IsFullyVisible(); + + virtual void SetPos(int x, int y); + virtual void GetPos(int &x, int &y); + virtual void SetSize(int wide,int tall); + virtual void GetSize(int& wide,int& tall); + virtual void SetMinimumSize(int wide,int tall); + virtual void GetMinimumSize(int& wide,int& tall); + virtual void SetZPos(int z); + virtual int GetZPos(); + + virtual void GetAbsPos(int &x, int &y); + virtual void GetClipRect(int &x0, int &y0, int &x1, int &y1); + virtual void SetInset(int left, int top, int right, int bottom); + virtual void GetInset(int &left, int &top, int &right, int &bottom); + + virtual void Solve(); + + virtual void SetVisible(bool state); + virtual void SetEnabled(bool state); + virtual bool IsVisible(); + virtual bool IsEnabled(); + virtual void SetParent(VPanel *newParent); + virtual int GetChildCount(); + virtual VPanel *GetChild(int index); + virtual VPanel *GetParent(); + virtual void MoveToFront(); + virtual void MoveToBack(); + virtual bool HasParent(VPanel *potentialParent); + + virtual CUtlVector< VPanel * > &GetChildren(); + + // gets names of the object (for debugging purposes) + virtual const char *GetName(); + virtual const char *GetClassName(); + + virtual HScheme GetScheme(); + + // handles a message + virtual void SendMessage(KeyValues *params, VPANEL ifromPanel); + + // wrapper to get Client panel interface + virtual IClientPanel *Client() { return _clientPanel; } + + // input interest + virtual void SetKeyBoardInputEnabled(bool state); + virtual void SetMouseInputEnabled(bool state); + virtual bool IsKeyBoardInputEnabled(); + virtual bool IsMouseInputEnabled(); + + virtual bool IsTopmostPopup() const; + virtual void SetTopmostPopup( bool bEnable ); + + // sibling pins + virtual void SetSiblingPin(VPanel *newSibling, byte iMyCornerToPin = 0, byte iSiblingCornerToPinTo = 0 ); + + public: + virtual void GetInternalAbsPos(int &x, int &y); + virtual void TraverseLevel( int val ); + + private: + Dar<VPanel*> _childDar; + VPanel *_parent; + SurfacePlat *_plat; // platform-specific data + HPanel _hPanel; + + // our companion Client panel + IClientPanel *_clientPanel; + + short _pos[2]; + short _size[2]; + short _minimumSize[2]; + + short _inset[4]; + short _clipRect[4]; + short _absPos[2]; + + short _zpos; // z-order position + + bool _visible : 1; + bool _enabled : 1; + bool _popup : 1; + bool _mouseInput : 1; // used for popups + bool _kbInput : 1; + bool _isTopmostPopup : 1; + + VPanel *_pinsibling; + byte _pinsibling_my_corner; + byte _pinsibling_their_corner; + + int m_nMessageContextId; + int m_nThinkTraverseLevel; + HPanel _clientPanelHandle; // Temp to check if _clientPanel is valid. + }; + +} + + +#endif // VPANEL_H diff --git a/vgui2/src/VPanelWrapper.cpp b/vgui2/src/VPanelWrapper.cpp new file mode 100644 index 0000000..d733e8c --- /dev/null +++ b/vgui2/src/VPanelWrapper.cpp @@ -0,0 +1,362 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <assert.h> + +#include "VPanel.h" +#include "vgui_internal.h" + +#include <vgui/IClientPanel.h> +#include <vgui/IPanel.h> +#include <vgui/ISurface.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Protects internal VPanel through the versionable interface IPanel +//----------------------------------------------------------------------------- +class VPanelWrapper : public vgui::IPanel +{ +public: + virtual void Init(VPANEL vguiPanel, IClientPanel *panel) + { + ((VPanel *)vguiPanel)->Init(panel); + } + + // returns a pointer to the Client panel + virtual IClientPanel *Client(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->Client(); + } + + // methods + virtual void SetPos(VPANEL vguiPanel, int x, int y) + { + ((VPanel *)vguiPanel)->SetPos(x, y); + } + + virtual void GetPos(VPANEL vguiPanel, int &x, int &y) + { + ((VPanel *)vguiPanel)->GetPos(x, y); + } + + virtual void SetSize(VPANEL vguiPanel, int wide,int tall) + { + ((VPanel *)vguiPanel)->SetSize(wide, tall); + } + + virtual void GetSize(VPANEL vguiPanel, int &wide, int &tall) + { + ((VPanel *)vguiPanel)->GetSize(wide, tall); + } + + virtual void SetMinimumSize(VPANEL vguiPanel, int wide, int tall) + { + ((VPanel *)vguiPanel)->SetMinimumSize(wide, tall); + } + + virtual void GetMinimumSize(VPANEL vguiPanel, int &wide, int &tall) + { + ((VPanel *)vguiPanel)->GetMinimumSize(wide, tall); + } + + virtual void SetZPos(VPANEL vguiPanel, int z) + { + ((VPanel *)vguiPanel)->SetZPos(z); + } + + virtual int GetZPos(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->GetZPos(); + } + + virtual void GetAbsPos(VPANEL vguiPanel, int &x, int &y) + { + ((VPanel *)vguiPanel)->GetAbsPos(x, y); + } + + virtual void GetClipRect(VPANEL vguiPanel, int &x0, int &y0, int &x1, int &y1) + { + ((VPanel *)vguiPanel)->GetClipRect(x0, y0, x1, y1); + } + + virtual void SetInset(VPANEL vguiPanel, int left, int top, int right, int bottom) + { + ((VPanel *)vguiPanel)->SetInset(left, top, right, bottom); + } + + virtual void GetInset(VPANEL vguiPanel, int &left, int &top, int &right, int &bottom) + { + ((VPanel *)vguiPanel)->GetInset(left, top, right, bottom); + } + + virtual void SetVisible(VPANEL vguiPanel, bool state) + { + ((VPanel *)vguiPanel)->SetVisible(state); + } + + virtual void SetEnabled(VPANEL vguiPanel, bool state) + { + ((VPanel *)vguiPanel)->SetEnabled(state); + } + + virtual bool IsVisible(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->IsVisible(); + } + + virtual bool IsEnabled(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->IsEnabled(); + } + + // Used by the drag/drop manager to always draw on top + virtual bool IsTopmostPopup( VPANEL vguiPanel ) + { + return ((VPanel *)vguiPanel)->IsTopmostPopup(); + } + + virtual void SetTopmostPopup( VPANEL vguiPanel, bool state ) + { + return ((VPanel *)vguiPanel)->SetTopmostPopup( state ); + } + + virtual void SetParent(VPANEL vguiPanel, VPANEL newParent) + { + ((VPanel *)vguiPanel)->SetParent((VPanel *)newParent); + } + + virtual int GetChildCount(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->GetChildCount(); + } + + virtual VPANEL GetChild(VPANEL vguiPanel, int index) + { + return (VPANEL)((VPanel *)vguiPanel)->GetChild(index); + } + + virtual CUtlVector< VPANEL > &GetChildren( VPANEL vguiPanel ) + { + return (CUtlVector< VPANEL > &)((VPanel *)vguiPanel)->GetChildren(); + } + + virtual VPANEL GetParent(VPANEL vguiPanel) + { + return (VPANEL)((VPanel *)vguiPanel)->GetParent(); + } + + virtual void MoveToFront(VPANEL vguiPanel) + { + ((VPanel *)vguiPanel)->MoveToFront(); + } + + virtual void MoveToBack(VPANEL vguiPanel) + { + ((VPanel *)vguiPanel)->MoveToBack(); + } + + virtual bool HasParent(VPANEL vguiPanel, VPANEL potentialParent) + { + if (!vguiPanel) + return false; + + return ((VPanel *)vguiPanel)->HasParent((VPanel *)potentialParent); + } + + virtual bool IsPopup(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->IsPopup(); + } + + virtual void SetPopup(VPANEL vguiPanel, bool state) + { + ((VPanel *)vguiPanel)->SetPopup(state); + } + + virtual bool IsFullyVisible( VPANEL vguiPanel ) + { + return ((VPanel *)vguiPanel)->IsFullyVisible(); + } + + // calculates the panels current position within the hierarchy + virtual void Solve(VPANEL vguiPanel) + { + ((VPanel *)vguiPanel)->Solve(); + } + + // used by ISurface to store platform-specific data + virtual SurfacePlat *Plat(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->Plat(); + } + + virtual void SetPlat(VPANEL vguiPanel, SurfacePlat *Plat) + { + ((VPanel *)vguiPanel)->SetPlat(Plat); + } + + virtual const char *GetName(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->GetName(); + } + + virtual const char *GetClassName(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->GetClassName(); + } + + virtual HScheme GetScheme(VPANEL vguiPanel) + { + return ((VPanel *)vguiPanel)->GetScheme(); + } + + virtual bool IsProportional(VPANEL vguiPanel) + { + return Client(vguiPanel)->IsProportional(); + } + + virtual bool IsAutoDeleteSet(VPANEL vguiPanel) + { + return Client(vguiPanel)->IsAutoDeleteSet(); + } + + virtual void DeletePanel(VPANEL vguiPanel) + { + Client(vguiPanel)->DeletePanel(); + } + + virtual void SendMessage(VPANEL vguiPanel, KeyValues *params, VPANEL ifrompanel) + { + ((VPanel *)vguiPanel)->SendMessage(params, ifrompanel); + } + + virtual void Think(VPANEL vguiPanel) + { + Client(vguiPanel)->Think(); + } + + virtual void PerformApplySchemeSettings(VPANEL vguiPanel) + { + Client(vguiPanel)->PerformApplySchemeSettings(); + } + + virtual void PaintTraverse(VPANEL vguiPanel, bool forceRepaint, bool allowForce) + { + Client(vguiPanel)->PaintTraverse(forceRepaint, allowForce); + } + + virtual void Repaint(VPANEL vguiPanel) + { + Client(vguiPanel)->Repaint(); + } + + virtual VPANEL IsWithinTraverse(VPANEL vguiPanel, int x, int y, bool traversePopups) + { + return Client(vguiPanel)->IsWithinTraverse(x, y, traversePopups); + } + + virtual void OnChildAdded(VPANEL vguiPanel, VPANEL child) + { + Client(vguiPanel)->OnChildAdded(child); + } + + virtual void OnSizeChanged(VPANEL vguiPanel, int newWide, int newTall) + { + Client(vguiPanel)->OnSizeChanged(newWide, newTall); + } + + virtual void InternalFocusChanged(VPANEL vguiPanel, bool lost) + { + Client(vguiPanel)->InternalFocusChanged(lost); + } + + virtual bool RequestInfo(VPANEL vguiPanel, KeyValues *outputData) + { + return Client(vguiPanel)->RequestInfo(outputData); + } + + virtual void RequestFocus(VPANEL vguiPanel, int direction = 0) + { + Client(vguiPanel)->RequestFocus(direction); + } + + virtual bool RequestFocusPrev(VPANEL vguiPanel, VPANEL existingPanel) + { + return Client(vguiPanel)->RequestFocusPrev(existingPanel); + } + + virtual bool RequestFocusNext(VPANEL vguiPanel, VPANEL existingPanel) + { + return Client(vguiPanel)->RequestFocusNext(existingPanel); + } + + virtual VPANEL GetCurrentKeyFocus(VPANEL vguiPanel) + { + return Client(vguiPanel)->GetCurrentKeyFocus(); + } + + virtual int GetTabPosition(VPANEL vguiPanel) + { + return Client(vguiPanel)->GetTabPosition(); + } + + virtual Panel *GetPanel(VPANEL vguiPanel, const char *moduleName) + { + if (!vguiPanel) + return NULL; + + if (vguiPanel == g_pSurface->GetEmbeddedPanel()) + return NULL; + + // assert that the specified vpanel is from the same module as requesting the cast + if ( !vguiPanel || V_stricmp(GetModuleName(vguiPanel), moduleName) ) + { + // assert(!("GetPanel() used to retrieve a Panel * from a different dll than which which it was created. This is bad, you can't pass Panel * across dll boundaries else you'll break the versioning. Please only use a VPANEL.")); + // this is valid for now + return NULL; + } + return Client(vguiPanel)->GetPanel(); + } + + virtual const char *GetModuleName(VPANEL vguiPanel) + { + return Client(vguiPanel)->GetModuleName(); + } + + virtual void SetKeyBoardInputEnabled( VPANEL vguiPanel, bool state ) + { + ((VPanel *)vguiPanel)->SetKeyBoardInputEnabled(state); + } + + virtual void SetMouseInputEnabled( VPANEL vguiPanel, bool state ) + { + ((VPanel *)vguiPanel)->SetMouseInputEnabled(state); + } + + virtual bool IsMouseInputEnabled( VPANEL vguiPanel ) + { + return ((VPanel *)vguiPanel)->IsMouseInputEnabled(); + } + + virtual bool IsKeyBoardInputEnabled( VPANEL vguiPanel ) + { + return ((VPanel *)vguiPanel)->IsKeyBoardInputEnabled(); + } + + virtual void SetSiblingPin(VPANEL vguiPanel, VPANEL newSibling, byte iMyCornerToPin = 0, byte iSiblingCornerToPinTo = 0 ) + { + return ((VPanel *)vguiPanel)->SetSiblingPin( (VPanel *)newSibling, iMyCornerToPin, iSiblingCornerToPinTo ); + } + +}; + +EXPOSE_SINGLE_INTERFACE(VPanelWrapper, IPanel, VGUI_PANEL_INTERFACE_VERSION); + diff --git a/vgui2/src/bitmap.h b/vgui2/src/bitmap.h new file mode 100644 index 0000000..f75275d --- /dev/null +++ b/vgui2/src/bitmap.h @@ -0,0 +1,64 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BITMAP_H +#define BITMAP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/IImage.h> +#include <Color.h> + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Holds a single image, internal to vgui only +//----------------------------------------------------------------------------- +class Bitmap : public IImage +{ +public: + Bitmap( const char *filename, bool hardwareFiltered ); + ~Bitmap(); + + // IImage implementation + virtual void Paint(); + virtual void GetSize( int &wide, int &tall ); + virtual void GetContentSize( int &wide, int &tall ); + virtual void SetSize( int x, int y ); + virtual void SetPos( int x, int y ); + virtual void SetColor( Color col ); + virtual bool Evict(); + virtual int GetNumFrames(); + virtual void SetFrame( int nFrame ); + virtual HTexture GetID(); // returns the texture id + virtual void SetRotation( int iRotation ) { _rotation = iRotation; } + + // methods + void ForceUpload(); // ensures the bitmap has been uploaded + const char *GetName(); + bool IsValid() { return _valid; } + +private: + HTexture _id; + bool _uploaded; + bool _valid; + char *_filename; + int _pos[2]; + Color _color; + bool _filtered; + int _wide,_tall; + bool _bProcedural; + unsigned int nFrameCache; + int _rotation; +}; + +} // namespace vgui + +#endif // BITMAP_H diff --git a/vgui2/src/fileimage.cpp b/vgui2/src/fileimage.cpp new file mode 100644 index 0000000..b28c50a --- /dev/null +++ b/vgui2/src/fileimage.cpp @@ -0,0 +1,211 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <string.h> +#include "fileimage.h" + +#include "winlite.h" +#include "vgui_internal.h" +#include "filesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// TGA header. +#pragma pack(1) + class TGAFileHeader + { + public: + unsigned char m_IDLength; + unsigned char m_ColorMapType; + unsigned char m_ImageType; + unsigned short m_CMapStart; + unsigned short m_CMapLength; + unsigned char m_CMapDepth; + unsigned short m_XOffset; + unsigned short m_YOffset; + unsigned short m_Width; + unsigned short m_Height; + unsigned char m_PixelDepth; + unsigned char m_ImageDescriptor; + }; +#pragma pack() + + + +// ---------------------------------------------------------------------------------------- // +// FileImageStream_Memory. +// ---------------------------------------------------------------------------------------- // +FileImageStream_Memory::FileImageStream_Memory(void *pData, int dataLen) +{ + m_pData = (unsigned char*)pData; + m_DataLen = dataLen; + m_CurPos = 0; + m_bError = false; +} + +void FileImageStream_Memory::Read(void *pData, int len) +{ + unsigned char *pOut; + int i; + + pOut = (unsigned char*)pData; + for(i=0; i < len; i++) + { + if(m_CurPos < m_DataLen) + { + pOut[i] = m_pData[m_CurPos]; + ++m_CurPos; + } + else + { + pOut[i] = 0; + m_bError = true; + } + } +} + +bool FileImageStream_Memory::ErrorStatus() +{ + bool ret=m_bError; + m_bError=false; + return ret; +} + + + +// ---------------------------------------------------------------------------------------- // +// Encode/decode functions. +// ---------------------------------------------------------------------------------------- // +static void WriteRun(unsigned char *pColor, FileHandle_t fp, int runLength) +{ + unsigned char runCount; + + runCount = runLength - 1; + runCount |= (1 << 7); + g_pFullFileSystem->Write( &runCount, 1, fp ); + g_pFullFileSystem->Write( pColor, 4, fp ); +} + + +// Load in a 32-bit TGA file. +bool Load32BitTGA( + FileImageStream *fp, + FileImage *pImage) +{ + TGAFileHeader hdr; + char dummyChar; + int i, x, y; + long color; + int runLength, curOut; + unsigned char *pLine; + unsigned char packetHeader; + + + pImage->Term(); + + // Read and verify the header. + fp->Read(&hdr, sizeof(hdr)); + if(hdr.m_PixelDepth != 32 || hdr.m_ImageType != 10) + return false; + + // Skip the ID area.. + for(i=0; i < hdr.m_IDLength; i++) + fp->Read(&dummyChar, 1); + + pImage->m_Width = hdr.m_Width; + pImage->m_Height = hdr.m_Height; + pImage->m_pData = new unsigned char[hdr.m_Width * hdr.m_Height * 4]; + if(!pImage->m_pData) + return false; + + // Read in the data.. + for(y=pImage->m_Height-1; y >= 0; y--) + { + pLine = &pImage->m_pData[y*pImage->m_Width*4]; + + curOut = 0; + while(curOut < pImage->m_Width) + { + fp->Read(&packetHeader, 1); + + runLength = (int)(packetHeader & ~(1 << 7)) + 1; + if(curOut + runLength > pImage->m_Width) + return false; + + if(packetHeader & (1 << 7)) + { + fp->Read(&color, 4); + for(x=0; x < runLength; x++) + { + *((long*)pLine) = color; + pLine += 4; + } + } + else + { + for(x=0; x < runLength; x++) + { + fp->Read(&color, 4); + *((long*)pLine) = color; + pLine += 4; + } + } + + curOut += runLength; + } + } + + return true; +} + + +// Write a 32-bit TGA file. +void Save32BitTGA( + FileHandle_t fp, + FileImage *pImage) +{ + TGAFileHeader hdr; + int y, runStart, x; + unsigned char *pLine; + + + memset(&hdr, 0, sizeof(hdr)); + hdr.m_PixelDepth = 32; + hdr.m_ImageType = 10; // Run-length encoded RGB. + hdr.m_Width = pImage->m_Width; + hdr.m_Height = pImage->m_Height; + + g_pFullFileSystem->Write(&hdr, sizeof(hdr), fp ); + + // Lines are written bottom-up. + for(y=pImage->m_Height-1; y >= 0; y--) + { + pLine = &pImage->m_pData[y*pImage->m_Width*4]; + + runStart = 0; + for(x=0; x < pImage->m_Width; x++) + { + if((x - runStart) >= 128 || + *((long*)&pLine[runStart*4]) != *((long*)&pLine[x*4])) + { + // Encode this Run. + WriteRun(&pLine[runStart*4], fp, x - runStart); + runStart = x; + } + } + + // Encode the last Run. + if(x - runStart > 0) + { + WriteRun(&pLine[runStart*4], fp, x - runStart); + } + } +} + + diff --git a/vgui2/src/fileimage.h b/vgui2/src/fileimage.h new file mode 100644 index 0000000..182f0d5 --- /dev/null +++ b/vgui2/src/fileimage.h @@ -0,0 +1,95 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef __FILEIMAGE_H__ +#define __FILEIMAGE_H__ + +#ifdef _WIN32 +#pragma once +#endif + +#include <stdio.h> + +typedef void *FileHandle_t; + +class FileImageStream +{ +public: + virtual void Read(void *pOut, int len)=0; + + // Returns true if there were any Read errors. + // Clears error status. + virtual bool ErrorStatus()=0; +}; + + +// Use to read out of a memory buffer.. +class FileImageStream_Memory : public FileImageStream +{ +public: + FileImageStream_Memory(void *pData, int dataLen); + + virtual void Read(void *pOut, int len); + virtual bool ErrorStatus(); + + +private: + unsigned char *m_pData; + int m_DataLen; + int m_CurPos; + bool m_bError; +}; + + + +// Generic image representation.. +class FileImage +{ +public: + FileImage() + { + Clear(); + } + + ~FileImage() + { + Term(); + } + + void Term() + { + if(m_pData) + delete [] m_pData; + + Clear(); + } + + // Clear the structure without deallocating. + void Clear() + { + m_Width = m_Height = 0; + m_pData = NULL; + } + + int m_Width, m_Height; + unsigned char *m_pData; +}; + + +// Functions to load/save FileImages. +bool Load32BitTGA( + FileImageStream *fp, + FileImage *pImage); + +void Save32BitTGA( + FileHandle_t fp, + FileImage *pImage); + + +#endif + + diff --git a/vgui2/src/keyrepeat.cpp b/vgui2/src/keyrepeat.cpp new file mode 100644 index 0000000..db129e6 --- /dev/null +++ b/vgui2/src/keyrepeat.cpp @@ -0,0 +1,177 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "inputsystem/InputEnums.h" + +#include "vgui/KeyCode.h" +#include "vgui/keyrepeat.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file +#include "tier0/memdbgon.h" + +//#define DEBUG_REPEATS + +#ifdef DEBUG_REPEATS +#define DbgRepeat(...) ConMsg( __VA_ARGS__ ) +#else +#define DbgRepeat(...) +#endif + +using namespace vgui; + +vgui::KeyCode g_iCodesForAliases[FM_NUM_KEYREPEAT_ALIASES] = +{ + KEY_XBUTTON_UP, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + KEY_XBUTTON_RIGHT +}; + +//----------------------------------------------------------------------------- +// Purpose: Map joystick codes to our internal ones +//----------------------------------------------------------------------------- +static int GetIndexForCode( vgui::KeyCode code ) +{ + KeyCode localCode = GetBaseButtonCode( code ); + + switch ( localCode ) + { + case KEY_XBUTTON_DOWN: + case KEY_XSTICK1_DOWN: + case KEY_XSTICK2_DOWN: + return KR_ALIAS_DOWN; break; + case KEY_XBUTTON_UP: + case KEY_XSTICK1_UP: + case KEY_XSTICK2_UP: + return KR_ALIAS_UP; break; + case KEY_XBUTTON_LEFT: + case KEY_XSTICK1_LEFT: + case KEY_XSTICK2_LEFT: + return KR_ALIAS_LEFT; break; + case KEY_XBUTTON_RIGHT: + case KEY_XSTICK1_RIGHT: + case KEY_XSTICK2_RIGHT: + return KR_ALIAS_RIGHT; break; + default: + break; + } + return -1; +} + +//----------------------------------------------------------------------------- +CKeyRepeatHandler::CKeyRepeatHandler() +{ + Reset(); + for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) + { + m_flRepeatTimes[i] = 0.16; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Clear all state +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::Reset() +{ + DbgRepeat( "KeyRepeat: Reset\n" ); + + memset( m_bAliasDown, 0, sizeof( m_bAliasDown ) ); + m_bHaveKeyDown = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::KeyDown( vgui::KeyCode code ) +{ + int joyStick = GetJoystickForCode( code ); + int iIndex = GetIndexForCode(code); + if ( iIndex == -1 ) + return; + + if ( m_bAliasDown[ joyStick ][ iIndex ] ) + return; + + DbgRepeat( "KeyRepeat: KeyDown %d(%d)\n", joyStick, iIndex ); + + Reset(); + m_bAliasDown[ joyStick ][ iIndex ] = true; + m_flNextKeyRepeat[ joyStick ] = Plat_FloatTime() + 0.4; + m_bHaveKeyDown = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::KeyUp( vgui::KeyCode code ) +{ + int joyStick = GetJoystickForCode( code ); + int iIndex = GetIndexForCode(code); + if ( iIndex == -1 ) + return; + + DbgRepeat( "KeyRepeat: KeyUp %d(%d)\n", joyStick, iIndex ); + + m_bAliasDown[ joyStick ][ iIndex ] = false; + + m_bHaveKeyDown = false; + for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) + { + for ( int j = 0; j < MAX_JOYSTICKS; j++ ) + { + if ( m_bAliasDown[ j ][ i ] ) + { + m_bHaveKeyDown = true; + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +vgui::KeyCode CKeyRepeatHandler::KeyRepeated( void ) +{ + if ( IsPC() ) + return BUTTON_CODE_NONE; + + if ( !m_bHaveKeyDown ) + return BUTTON_CODE_NONE; + + float currentTime = Plat_FloatTime(); + + for ( int j = 0; j < MAX_JOYSTICKS; j++ ) + { + if ( m_flNextKeyRepeat[ j ] < currentTime ) + { + for ( int i = 0; i < FM_NUM_KEYREPEAT_ALIASES; i++ ) + { + if ( m_bAliasDown[ j ][ i ] ) + { + m_flNextKeyRepeat[ j ] = currentTime + m_flRepeatTimes[i]; + DbgRepeat( "KeyRepeat: Repeat %d(%d)\n", j, i ); + + return ButtonCodeToJoystickButtonCode( g_iCodesForAliases[i], j ); + } + } + } + } + + return BUTTON_CODE_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyRepeatHandler::SetKeyRepeatTime( vgui::KeyCode code, float flRepeat ) +{ + int iIndex = GetIndexForCode(code); + Assert( iIndex != -1 ); + m_flRepeatTimes[ iIndex ] = flRepeat; +} diff --git a/vgui2/src/system_posix.cpp b/vgui2/src/system_posix.cpp new file mode 100644 index 0000000..251cbb2 --- /dev/null +++ b/vgui2/src/system_posix.cpp @@ -0,0 +1,809 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/param.h> + +#include <vgui/VGUI.h> +#include <vgui/ISystem.h> +#include <KeyValues.h> +#include <vgui/IInputInternal.h> +#include <vgui/ISurface.h> +#include "tier0/vcrmode.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" + +#include "vgui_internal.h" +#include "filesystem_helpers.h" +#include "vgui_key_translation.h" +#include "filesystem.h" + +#ifdef OSX +#include <Carbon/Carbon.h> +#elif defined(LINUX) +#include <sys/vfs.h> +#endif + +#ifdef USE_SDL +#include "SDL_clipboard.h" +#include "SDL_error.h" +#endif + +#define PROTECTED_THINGS_DISABLE +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +using namespace vgui; + +uint16 System_GetKeyState( int virtualKeyCode ) +{ +#ifndef _XBOX + return g_pVCR->Hook_GetKeyState(virtualKeyCode); +#else + return 0; +#endif +} + +class CSystem : public ISystem +{ +public: + CSystem(); + ~CSystem(); + + virtual void Shutdown(); + virtual void RunFrame(); + + virtual long GetTimeMillis(); + + // returns the time at the start of the frame + virtual double GetFrameTime(); + + // returns the current time + virtual double GetCurrentTime(); + + virtual void ShellExecute(const char *command, const char *file); + + virtual int GetClipboardTextCount(); + virtual void SetClipboardText(const char *text, int textLen); + virtual void SetClipboardText(const wchar_t *text, int textLen); + virtual int GetClipboardText(int offset, char *buf, int bufLen); + virtual int GetClipboardText(int offset, wchar_t *buf, int bufLen); + + virtual void SetClipboardImage( void *pWnd, int x1, int y1, int x2, int y2 ); + + virtual bool SetRegistryString(const char *key, const char *value); + virtual bool GetRegistryString(const char *key, char *value, int valueLen); + virtual bool SetRegistryInteger(const char *key, int value); + virtual bool GetRegistryInteger(const char *key, int &value); + virtual bool DeleteRegistryKey(const char *keyName); + + virtual bool SetWatchForComputerUse(bool state); + virtual double GetTimeSinceLastUse(); + virtual int GetAvailableDrives(char *buf, int bufLen); + virtual double GetFreeDiskSpace(const char *path); + + virtual KeyValues *GetUserConfigFileData(const char *dialogName, int dialogID); + virtual void SetUserConfigFile(const char *fileName, const char *pathName); + virtual void SaveUserConfigFile(); + + virtual bool CommandLineParamExists(const char *commandName); + virtual bool GetCommandLineParamValue(const char *paramName, char *value, int valueBufferSize); + virtual const char *GetFullCommandLine(); + virtual bool GetCurrentTimeAndDate(int *year, int *month, int *dayOfWeek, int *day, int *hour, int *minute, int *second); + + // shortcut (.lnk) modification functions + virtual bool CreateShortcut(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory, const char *iconFile); + virtual bool GetShortcutTarget(const char *linkFileName, char *targetPath, char *arguments, int destBufferSizes); + virtual bool ModifyShortcutTarget(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory); + + virtual KeyCode KeyCode_VirtualKeyToVGUI( int keyCode ); + virtual int KeyCode_VGUIToVirtualKey( KeyCode keyCode ); +// virtual MouseCode MouseCode_VirtualKeyToVGUI( int keyCode ); +// virtual int MouseCode_VGUIToVirtualKey( MouseCode keyCode ); + virtual const char *GetDesktopFolderPath(); + virtual const char *GetStartMenuFolderPath(); + virtual const char *GetAllUserDesktopFolderPath(); + virtual const char *GetAllUserStartMenuFolderPath(); + + virtual void ShellExecuteEx( const char *command, const char *file, const char *pParams ); +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, char *pchName ); +#endif + +private: + void SaveRegistryToFile( bool bForce = false ); + bool m_bStaticWatchForComputerUse; + double m_StaticLastComputerUseTime; + int m_iStaticMouseOldX, m_iStaticMouseOldY; + // timer data + double m_flFrameTime; + KeyValues *m_pUserConfigData; + char m_szFileName[MAX_PATH]; + char m_szPathID[MAX_PATH]; + + KeyValues *m_pRegistry; + double m_flRegistrySaveTime; + bool m_bRegistryDirty; + + char m_szRegistryPath[ MAX_PATH ]; +#ifdef OSX + PasteboardRef m_PasteBoardRef; +#endif + +}; + + +CSystem g_System; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CSystem, ISystem, VGUI_SYSTEM_INTERFACE_VERSION, g_System); + +namespace vgui +{ +vgui::ISystem *g_pSystem = &g_System; +} + +#define REGISTRY_NAME "cfg/registry.vdf" +#define REGISTRY_SAVE_INTERVAL 30 + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSystem::CSystem() +{ + m_bStaticWatchForComputerUse = false; + m_flFrameTime = 0.0; + m_flRegistrySaveTime = 0.0; + m_bRegistryDirty = false; + m_pUserConfigData = NULL; +#ifdef OSX + PasteboardCreate( kPasteboardClipboard, &m_PasteBoardRef ); +#endif + + Q_snprintf( m_szRegistryPath, sizeof(m_szRegistryPath), "%s", REGISTRY_NAME ); + + m_pRegistry = new KeyValues( "registry" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSystem::~CSystem() +{ + SaveRegistryToFile( true ); +#ifdef OSX + CFRelease( m_PasteBoardRef ); +#endif +} + +void CSystem::SaveRegistryToFile( bool bForce ) +{ + /*if ( m_pRegistry && ( m_bRegistryDirty || bForce ) && g_pFullFileSystem ) + { + m_pRegistry->SaveToFile( g_pFullFileSystem, m_szRegistryPath, "MOD" ); + }*/ + m_bRegistryDirty = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSystem::Shutdown() +{ + if (m_pUserConfigData) + { + m_pUserConfigData->deleteThis(); + } + SaveRegistryToFile( true ); + if ( m_pRegistry ) + { + m_pRegistry->deleteThis(); + } + m_pRegistry = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Handles all the per frame actions +//----------------------------------------------------------------------------- +void CSystem::RunFrame() +{ + // record the current frame time + m_flFrameTime = GetCurrentTime(); + + if (m_bStaticWatchForComputerUse) + { + // check for mouse movement + int x, y; + g_pInput->GetCursorPos(x, y); + // allow a little slack for jittery mice, don't reset until it's moved more than fifty pixels + if (abs((x + y) - (m_iStaticMouseOldX + m_iStaticMouseOldY)) > 50) + { + m_StaticLastComputerUseTime = Plat_MSTime(); + m_iStaticMouseOldX = x; + m_iStaticMouseOldY = y; + } + } + + if ( m_flFrameTime - m_flRegistrySaveTime > REGISTRY_SAVE_INTERVAL ) + { + m_flRegistrySaveTime = m_flFrameTime; + SaveRegistryToFile(); +// Registry_RunFrame(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns the time at the start of the frame +//----------------------------------------------------------------------------- +double CSystem::GetFrameTime() +{ + return m_flFrameTime; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the current time +//----------------------------------------------------------------------------- +double CSystem::GetCurrentTime() +{ + return Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the current time in milliseconds +//----------------------------------------------------------------------------- +long CSystem::GetTimeMillis() +{ + return (long)(Plat_MSTime() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Legacy stub to allow ShellExecute( "open", "file" ) -- doesn't otherwise work +//----------------------------------------------------------------------------- +void CSystem::ShellExecute(const char *command, const char *file) +{ + if ( V_strcmp( command, "open" ) != 0 ) + { + // Nope + Assert( !"This legacy command is only supported in the form of open <foo>" ); + return; + } + +#ifdef OSX + const char *szCommand = "open"; +#else + const char *szCommand = "xdg-open"; +#endif + + pid_t pid = fork(); + if ( pid == 0 ) + { + // Child +#ifdef LINUX + // Escape steam runtime if necessary + const char *szSteamRuntime = getenv( "STEAM_RUNTIME" ); + if ( szSteamRuntime ) + { + unsetenv( "STEAM_RUNTIME" ); + + const char *szSystemLibraryPath = getenv( "SYSTEM_LD_LIBRARY_PATH" ); + const char *szSystemPath = getenv( "SYSTEM_PATH" ); + if ( szSystemLibraryPath ) + { + setenv( "LD_LIBRARY_PATH", szSystemLibraryPath, 1 ); + } + if ( szSystemPath ) + { + setenv( "PATH", szSystemPath, 1 ); + } + } +#endif + execlp( szCommand, szCommand, file, (char *)0 ); + Assert( !"execlp failed" ); + } +} + +void CSystem::ShellExecuteEx( const char *command, const char *file, const char *pParams ) +{ + NOTE_UNUSED( pParams ); + ShellExecute( command, file ); +} + +void CSystem::SetClipboardText(const char *text, int textLen) +{ +#ifdef OSX + PasteboardSynchronize( m_PasteBoardRef ); + PasteboardClear( m_PasteBoardRef ); + CFDataRef theData = CFDataCreate( kCFAllocatorDefault, (const UInt8*)text, textLen ); + PasteboardPutItemFlavor( m_PasteBoardRef, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), theData, 0 ); + CFRelease( theData ); +#elif defined( USE_SDL ) + if ( Q_strlen( text ) <= textLen ) + { + if ( SDL_SetClipboardText( text ) ) + { + Msg( "SDL_SetClipboardText failed: %s\n", SDL_GetError() ); + } + } + else + { + char *ClipText = ( char *)malloc( textLen + 1 ); + if ( ClipText ) + { + Q_strncpy( ClipText, text, textLen + 1 ); + if ( SDL_SetClipboardText( ClipText ) ) + { + Msg( "SDL_SetClipboardText failed: %s\n", SDL_GetError() ); + } + free( ClipText ); + } + } +#endif +} + +void CSystem::SetClipboardImage( void *pWnd, int x1, int y1, int x2, int y2 ) +{ + Assert( false ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Puts unicode text into the clipboard +//----------------------------------------------------------------------------- +void CSystem::SetClipboardText(const wchar_t *text, int textLen) +{ + char *charStr = (char *)malloc( textLen * 4 ); + + Q_UnicodeToUTF8( text, charStr, textLen*4 ); + +#ifdef OSX + PasteboardSynchronize( m_PasteBoardRef ); + PasteboardClear( m_PasteBoardRef ); + + CFDataRef theData = CFDataCreate( kCFAllocatorDefault, (const UInt8*)charStr, Q_strlen(charStr) ); + PasteboardPutItemFlavor( m_PasteBoardRef, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), theData, 0 ); + CFRelease( theData ); +#elif defined( USE_SDL ) + SetClipboardText( charStr, Q_strlen( charStr ) ); +#endif + + free( charStr ); +} + +int CSystem::GetClipboardTextCount() +{ +#ifdef OSX + ItemCount count; + PasteboardSynchronize( m_PasteBoardRef ); + + OSStatus err = PasteboardGetItemCount( m_PasteBoardRef, &count ); + if ( err != noErr ) + return 0; + + if ( count <= 0 ) + return 0; + + PasteboardItemID ItemID; + // always use the last item on the clipboard for any cut and paste data + err = PasteboardGetItemIdentifier( m_PasteBoardRef, count, &ItemID ); + if ( err != noErr ) + return 0; + CFDataRef outData; + err = PasteboardCopyItemFlavorData ( m_PasteBoardRef, ItemID, CFSTR ("public.utf8-plain-text"), &outData); + if ( err != noErr ) + return 0; + + int copyLen = CFDataGetLength( outData ); + CFRelease( outData ); + return (int)copyLen + 1; +#elif defined( USE_SDL ) + int Count = 0; + + if ( SDL_HasClipboardText() ) + { + char *text = SDL_GetClipboardText(); + + if ( text ) + { + Count = Q_strlen( text ) + 1; + SDL_free( text ); + } + } + + return Count; +#else + return 0; +#endif +} + +int CSystem::GetClipboardText(int offset, char *buf, int bufLen) +{ + Assert( !offset ); + +#ifdef OSX + ItemCount count; + PasteboardSynchronize( m_PasteBoardRef ); + + OSStatus err = PasteboardGetItemCount( m_PasteBoardRef, &count ); + if ( err != noErr ) + return 0; + + char *pchOutData; + PasteboardItemID ItemID; + // pull the last item from the clipboard + err = PasteboardGetItemIdentifier( m_PasteBoardRef, count, &ItemID ); + if ( err != noErr ) + return 0; + CFDataRef outData; + err = PasteboardCopyItemFlavorData ( m_PasteBoardRef, ItemID, CFSTR ("public.utf8-plain-text"), &outData); + if ( err != noErr ) + return 0; + pchOutData = (char *)CFDataGetBytePtr(outData ); + int copyLen = MIN( CFDataGetLength( outData ), bufLen ) ; + if ( pchOutData ) + memcpy( buf, pchOutData, copyLen ); + CFRelease( outData ); + return copyLen; +#elif defined( USE_SDL ) + if( SDL_HasClipboardText() ) + { + char *text = SDL_GetClipboardText(); + + if ( text ) + { + Q_strncpy( buf, text, bufLen ); + SDL_free( text ); + return Q_strlen( buf ); + } + } + + return 0; +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Retrieves unicode text from the clipboard +//----------------------------------------------------------------------------- +int CSystem::GetClipboardText(int offset, wchar_t *buf, int bufLen) +{ + Assert( !offset ); + + char *outputUTF8 = (char *)malloc( bufLen*4 ); + int ret = GetClipboardText( offset, outputUTF8, bufLen ); + + if ( ret ) + { + Q_UTF8ToUnicode( outputUTF8, buf, bufLen ); + } + else if( bufLen > 0 ) + { + buf[ 0 ] = 0; + } + + free( outputUTF8 ); + return ret; +} + + +bool CSystem::SetRegistryString(const char *key, const char *value) +{ + m_bRegistryDirty = true; + m_pRegistry->SetString( key, value ); + return true; +} + +bool CSystem::GetRegistryString(const char *key, char *value, int valueLen) +{ + const char *pchVal = m_pRegistry->GetString( key ); + if ( pchVal ) + Q_strncpy( value, pchVal, valueLen ); + return pchVal != NULL; +} + +bool CSystem::SetRegistryInteger(const char *key, int value) +{ + m_bRegistryDirty = true; + m_pRegistry->SetInt( key, value ); + return false; +} + +bool CSystem::GetRegistryInteger(const char *key, int &value) +{ + value = m_pRegistry->GetInt( key ); + return value != 0; +} + +//----------------------------------------------------------------------------- +// Purpose: recursively deletes a registry key and all it's subkeys +//----------------------------------------------------------------------------- +bool CSystem::DeleteRegistryKey(const char *key) +{ + Assert( false ); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: sets whether or not the app watches for global computer use +//----------------------------------------------------------------------------- +bool CSystem::SetWatchForComputerUse(bool state) +{ + if (state == m_bStaticWatchForComputerUse) + return true; + + m_bStaticWatchForComputerUse = state; + + if (m_bStaticWatchForComputerUse) + { + // enable watching + } + else + { + // disable watching + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the time, in seconds, since the last computer use. +//----------------------------------------------------------------------------- +double CSystem::GetTimeSinceLastUse() +{ + if (m_bStaticWatchForComputerUse) + { + return ( Plat_MSTime() - m_StaticLastComputerUseTime ) / 1000.0f; + } + + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the drives a user has available on thier system +//----------------------------------------------------------------------------- +int CSystem::GetAvailableDrives(char *buf, int bufLen) +{ + Assert( false ); + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the amount of available disk space, in bytes, on the specified path +//----------------------------------------------------------------------------- +double CSystem::GetFreeDiskSpace(const char *path) +{ + struct statfs64 buf; + int ret = statfs64( path, &buf ); + if ( ret < 0 ) + return 0.0; + return (double) ( buf.f_bsize * buf.f_bfree ); +} + +//----------------------------------------------------------------------------- +// Purpose: user config +//----------------------------------------------------------------------------- +KeyValues *CSystem::GetUserConfigFileData(const char *dialogName, int dialogID) +{ + if (!m_pUserConfigData) + return NULL; + + Assert(dialogName && *dialogName); + + if (dialogID) + { + char buf[256]; + Q_snprintf(buf, sizeof(buf), "%s_%d", dialogName, dialogID); + dialogName = buf; + } + + return m_pUserConfigData->FindKey(dialogName, true); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the name of the config file to save/restore from. Settings are loaded immediately. +//----------------------------------------------------------------------------- +void CSystem::SetUserConfigFile(const char *fileName, const char *pathName) +{ + //m_pRegistry->LoadFromFile( g_pFullFileSystem, m_szRegistryPath, NULL ); + + if (!m_pUserConfigData) + { + m_pUserConfigData = new KeyValues("UserConfigData"); + } + else + { + // delete all the existing keys so when we reload from the new file we don't + // get duplicate entries in our key value + m_pUserConfigData->Clear(); + } + + Q_strncpy(m_szFileName, fileName, sizeof(m_szFileName)); + Q_strncpy(m_szPathID, pathName, sizeof(m_szPathID)); + + // open + m_pUserConfigData->UsesEscapeSequences( true ); // VGUI may use this + m_pUserConfigData->LoadFromFile(g_pFullFileSystem, m_szFileName, m_szPathID); +} + +//----------------------------------------------------------------------------- +// Purpose: saves all the current settings to the user config file +//----------------------------------------------------------------------------- +void CSystem::SaveUserConfigFile() +{ + if (m_pUserConfigData) + { + m_pUserConfigData->SaveToFile(g_pFullFileSystem, m_szFileName, m_szPathID); + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns whether or not the parameter was on the command line +//----------------------------------------------------------------------------- +bool CSystem::CommandLineParamExists(const char *paramName) +{ + if ( Q_strstr( Plat_GetCommandLine(), paramName ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the string following a command line param +//----------------------------------------------------------------------------- +bool CSystem::GetCommandLineParamValue(const char *paramName, char *value, int valueBufferSize) +{ + Assert( false ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name of the currently running exe +//----------------------------------------------------------------------------- +const char *CSystem::GetFullCommandLine() +{ + return VCRHook_GetCommandLine(); +} + + +KeyCode CSystem::KeyCode_VirtualKeyToVGUI( int keyCode ) +{ + return ::KeyCode_VirtualKeyToVGUI( keyCode ); +} + +int CSystem::KeyCode_VGUIToVirtualKey( KeyCode keyCode ) +{ + return ::KeyCode_VGUIToVirtualKey( keyCode ); +} + +/*MouseCode CSystem::MouseCode_VirtualKeyToVGUI( int keyCode ) +{ + return ::MouseCode_VirtualKeyToVGUI( keyCode ); +} + +int CSystem::MouseCode_VGUIToVirtualKey( MouseCode mouseCode ) +{ + return ::MouseCode_VGUIToVirtualKey( mouseCode ); +}*/ + + +//----------------------------------------------------------------------------- +// Purpose: returns the current local time and date +//----------------------------------------------------------------------------- +bool CSystem::GetCurrentTimeAndDate(int *year, int *month, int *dayOfWeek, int *day, int *hour, int *minute, int *second) +{ + time_t t = time( NULL ); + struct tm *now = localtime( &t ); + if ( now ) + { + if ( year ) *year = now->tm_year + 1900; + if ( month ) *month = now->tm_mon + 1; + if ( dayOfWeek ) *dayOfWeek = now->tm_wday; + if ( day ) *day = now->tm_mday; + if ( hour ) *hour = now->tm_hour; + if ( minute ) *minute = now->tm_min; + if ( second ) *second = now->tm_sec; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates a shortcut file +//----------------------------------------------------------------------------- +bool CSystem::CreateShortcut(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory, const char *iconFile) +{ + Assert( false ); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: retrieves shortcut (.lnk) information +//----------------------------------------------------------------------------- +bool CSystem::GetShortcutTarget(const char *linkFileName, char *targetPath, char *arguments, int destBufferSizes) +{ + Assert( false ); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: sets shortcut (.lnk) information +//----------------------------------------------------------------------------- +bool CSystem::ModifyShortcutTarget(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory) +{ + Assert( false ); + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the full path of the current user's desktop folder +//----------------------------------------------------------------------------- +const char *CSystem::GetDesktopFolderPath() +{ + Assert( false ); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the full path of the all user's desktop folder +//----------------------------------------------------------------------------- +const char *CSystem::GetAllUserDesktopFolderPath() +{ + Assert( false ); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the full path of the current user's start->program files +//----------------------------------------------------------------------------- +const char *CSystem::GetStartMenuFolderPath() +{ + Assert( false ); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the full path of the all user's start->program files +//----------------------------------------------------------------------------- +const char *CSystem::GetAllUserStartMenuFolderPath() +{ + Assert( false ); + return NULL; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Ensure that all of our internal structures are consistent, and +// account for all memory that we've allocated. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +#ifdef DBGFLAG_VALIDATE +void CSystem::Validate( CValidator &validator, char *pchName ) +{ + VALIDATE_SCOPE(); + ValidatePtr( m_pUserConfigData ); +} + + +void Validate_System( CValidator &validator ) +{ + ValidateObj( g_System ); +} +#endif diff --git a/vgui2/src/vgui.cpp b/vgui2/src/vgui.cpp new file mode 100644 index 0000000..3508b1a --- /dev/null +++ b/vgui2/src/vgui.cpp @@ -0,0 +1,1195 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Core implementation of vgui +// +// $NoKeywords: $ +//===========================================================================// + + +#if defined( WIN32 ) && !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#include "VGuiMatSurface/IMatSystemSurface.h" +#include <vgui/VGUI.h> +#include <vgui/Dar.h> +#include <vgui/IInputInternal.h> +#include <vgui/IPanel.h> +#include <vgui/ISystem.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/IClientPanel.h> +#include <vgui/IScheme.h> +#include <KeyValues.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <malloc.h> +#include <tier0/dbg.h> +#include <tier1/utlhandletable.h> +#include "vgui_internal.h" +#include "VPanel.h" +#include "IMessageListener.h" +#include "tier3/tier3.h" +#include "utllinkedlist.h" +#include "utlpriorityqueue.h" +#include "utlvector.h" +#include "tier0/vprof.h" +#include "tier0/icommandline.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +#undef GetCursorPos // protected_things.h defines this, and it makes it so we can't access g_pInput->GetCursorPos. + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +using namespace vgui; +static const int WARN_PANEL_NUMBER = 32768; // in DEBUG if more panels than this are created then throw an Assert, helps catch panel leaks + + +//----------------------------------------------------------------------------- +// Purpose: Single item in the message queue +//----------------------------------------------------------------------------- +struct MessageItem_t +{ + KeyValues *_params; // message data + // _params->GetName() is the message name + + HPanel _messageTo; // the panel this message is to be sent to + HPanel _from; // the panel this message is from (if any) + float _arrivalTime; // time at which the message should be passed on to the recipient + + int _messageID; // incrementing message index +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool PriorityQueueComp(const MessageItem_t& x, const MessageItem_t& y) +{ + if (x._arrivalTime > y._arrivalTime) + { + return true; + } + else if (x._arrivalTime < y._arrivalTime) + { + return false; + } + + // compare messageID's to ensure we have the messages in the correct order + return (x._messageID > y._messageID); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Implementation of core vgui functionality +//----------------------------------------------------------------------------- +class CVGui : public CTier3AppSystem< IVGui > +{ + typedef CTier3AppSystem< IVGui > BaseClass; + +public: + CVGui(); + ~CVGui(); + +//----------------------------------------------------------------------------- + // SRC specific stuff + // Here's where the app systems get to learn about each other + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + + // Here's where systems can access other interfaces implemented by this object + // Returns NULL if it doesn't implement the requested interface + virtual void *QueryInterface( const char *pInterfaceName ); + + // Init, shutdown + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + // End of specific interface +//----------------------------------------------------------------------------- + + + virtual void RunFrame(); + + virtual void Start() + { + m_bRunning = true; + } + + // signals vgui to Stop running + virtual void Stop() + { + m_bRunning = false; + } + + // returns true if vgui is current active + virtual bool IsRunning() + { + return m_bRunning; + } + + virtual void ShutdownMessage(unsigned int shutdownID); + + // safe-pointer handle methods + virtual VPANEL AllocPanel(); + virtual void FreePanel(VPANEL ipanel); + virtual HPanel PanelToHandle(VPANEL panel); + virtual VPANEL HandleToPanel(HPanel index); + virtual void MarkPanelForDeletion(VPANEL panel); + + virtual void AddTickSignal(VPANEL panel, int intervalMilliseconds = 0); + virtual void AddTickSignalToHead( VPANEL panel, int intervalMilliseconds = 0 ) OVERRIDE; + virtual void RemoveTickSignal(VPANEL panel ); + + + // message pump method + virtual void PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delaySeconds = 0.0f); + + virtual void SetSleep( bool state ) { m_bDoSleep = state; }; + virtual bool GetShouldVGuiControlSleep() { return m_bDoSleep; } + + virtual void DPrintf(const char *format, ...); + virtual void DPrintf2(const char *format, ...); + virtual void SpewAllActivePanelNames(); + + // Creates/ destroys vgui contexts, which contains information + // about which controls have mouse + key focus, for example. + virtual HContext CreateContext(); + virtual void DestroyContext( HContext context ); + + // Associates a particular panel with a vgui context + // Associating NULL is valid; it disconnects the panel from the context + virtual void AssociatePanelWithContext( HContext context, VPANEL pRoot ); + + // Activates a particular input context, use DEFAULT_VGUI_CONTEXT + // to get the one normally used by VGUI + virtual void ActivateContext( HContext context ); + + // enables VR mode + virtual void SetVRMode( bool bVRMode ) OVERRIDE + { + m_bVRMode = bVRMode; + } + virtual bool GetVRMode() OVERRIDE + { + return m_bVRMode; + } + + bool IsDispatchingMessages( void ) + { + return m_InDispatcher; + } + +private: + // VGUI contexts + struct Context_t + { + HInputContext m_hInputContext; + }; + + struct Tick_t + { + VPanel *panel; + int interval; + int nexttick; + bool bMarkDeleted; + // Debugging + char panelname[ 64 ]; + }; + + Tick_t* CreateNewTick( VPANEL panel, int intervalMilliseconds ); + + // Returns the current context + Context_t *GetContext( HContext context ); + + void PanelCreated(VPanel *panel); + void PanelDeleted(VPanel *panel); + bool DispatchMessages(); + void DestroyAllContexts( ); + void ClearMessageQueues(); + inline bool IsReentrant() const + { + return m_nReentrancyCount > 0; + } + + // safe panel handle stuff + CUtlHandleTable< VPanel, 20 > m_HandleTable; + int m_iCurrentMessageID; + + bool m_bRunning : 1; + bool m_bDoSleep : 1; + bool m_InDispatcher : 1; + bool m_bDebugMessages : 1; + bool m_bVRMode : 1; + bool m_bCanRemoveTickSignal : 1; + int m_nReentrancyCount; + + CUtlVector< Tick_t * > m_TickSignalVec; + CUtlLinkedList< Context_t > m_Contexts; + + HContext m_hContext; + Context_t m_DefaultContext; + +#ifdef DEBUG + int m_iDeleteCount, m_iDeletePanelCount; +#endif + + // message queue. holds all vgui messages generated by windows events + CUtlLinkedList<MessageItem_t, ushort> m_MessageQueue; + + // secondary message queue, holds all vgui messages generated by vgui + CUtlLinkedList<MessageItem_t, ushort> m_SecondaryQueue; + + // timing queue, holds all the messages that have to arrive at a specified time + CUtlPriorityQueue<MessageItem_t> m_DelayedMessageQueue; +}; + +CVGui g_VGui; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CVGui, IVGui, VGUI_IVGUI_INTERFACE_VERSION, g_VGui); + +bool IsDispatchingMessageQueue( void ) +{ + return g_VGui.IsDispatchingMessages(); +} + +namespace vgui +{ +IVGui *g_pIVgui = &g_VGui; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CVGui::CVGui() : m_DelayedMessageQueue(0, 4, PriorityQueueComp) +{ + m_bRunning = false; + m_InDispatcher = false; + m_bDebugMessages = false; + m_bDoSleep = true; + m_bVRMode = false; + m_bCanRemoveTickSignal = true; + m_nReentrancyCount = 0; + m_hContext = DEFAULT_VGUI_CONTEXT; + m_DefaultContext.m_hInputContext = DEFAULT_INPUT_CONTEXT; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CVGui::~CVGui() +{ +#ifdef _DEBUG + int nCount = m_HandleTable.GetHandleCount(); + int nActualCount = 0; + for ( int i = 0; i < nCount; ++i ) + { + UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i ); + if ( m_HandleTable.IsHandleValid( h ) ) + { + ++nActualCount; + } + } + + if ( nActualCount > 0 ) + { + Msg("Memory leak: panels left in CVGui::m_PanelList: %d\n", nActualCount ); + } +#endif // _DEBUG +} + +//----------------------------------------------------------------------------- +// Purpose: Dumps out list of all active panels +//----------------------------------------------------------------------------- +void CVGui::SpewAllActivePanelNames() +{ + int nCount = m_HandleTable.GetHandleCount(); + for ( int i = 0; i < nCount; ++i ) + { + UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i ); + if ( m_HandleTable.IsHandleValid( h ) ) + { + VPanel *pPanel = m_HandleTable.GetHandle( h ); + Msg("\tpanel '%s' of type '%s' leaked\n", g_pIPanel->GetName( (VPANEL)pPanel ), ((VPanel *)pPanel)->GetClassName()); + } + } +} + + +//----------------------------------------------------------------------------- +// Creates/ destroys "input" contexts, which contains information +// about which controls have mouse + key focus, for example. +//----------------------------------------------------------------------------- +HContext CVGui::CreateContext() +{ + HContext i = m_Contexts.AddToTail(); + m_Contexts[i].m_hInputContext = g_pInput->CreateInputContext(); + return i; +} + +void CVGui::DestroyContext( HContext context ) +{ + Assert( context != DEFAULT_VGUI_CONTEXT ); + + if ( m_hContext == context ) + { + ActivateContext( DEFAULT_VGUI_CONTEXT ); + } + + g_pInput->DestroyInputContext( GetContext(context)->m_hInputContext ); + m_Contexts.Remove(context); +} + +void CVGui::DestroyAllContexts( ) +{ + HContext next; + HContext i = m_Contexts.Head(); + while (i != m_Contexts.InvalidIndex()) + { + next = m_Contexts.Next(i); + DestroyContext( i ); + i = next; + } +} + + +//----------------------------------------------------------------------------- +// Returns the current context +//----------------------------------------------------------------------------- +CVGui::Context_t *CVGui::GetContext( HContext context ) +{ + if (context == DEFAULT_VGUI_CONTEXT) + return &m_DefaultContext; + return &m_Contexts[context]; +} + + +//----------------------------------------------------------------------------- +// Associates a particular panel with a context +// Associating NULL is valid; it disconnects the panel from the context +//----------------------------------------------------------------------------- +void CVGui::AssociatePanelWithContext( HContext context, VPANEL pRoot ) +{ + Assert( context != DEFAULT_VGUI_CONTEXT ); + g_pInput->AssociatePanelWithInputContext( GetContext(context)->m_hInputContext, pRoot ); +} + + +//----------------------------------------------------------------------------- +// Activates a particular context, use DEFAULT_VGUI_CONTEXT +// to get the one normally used by VGUI +//----------------------------------------------------------------------------- +void CVGui::ActivateContext( HContext context ) +{ + Assert( (context == DEFAULT_VGUI_CONTEXT) || m_Contexts.IsValidIndex(context) ); + + if ( m_hContext != context ) + { + // Clear out any messages queues that may be full... + if ( !IsReentrant() ) + { + DispatchMessages(); + } + + m_hContext = context; + g_pInput->ActivateInputContext( GetContext(m_hContext)->m_hInputContext ); + + if ( context != DEFAULT_VGUI_CONTEXT && !IsReentrant() ) + { + g_pInput->RunFrame( ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Runs a single vgui frame, pumping all message to panels +//----------------------------------------------------------------------------- +void CVGui::RunFrame() +{ + // NOTE: This can happen when running in Maya waiting for modal dialogs + bool bIsReentrant = m_InDispatcher; + if ( bIsReentrant ) + { + ++m_nReentrancyCount; + } + +#ifdef DEBUG +// memory allocation debug helper +// DPrintf( "Delete Count:%i,%i\n", m_iDeleteCount, m_iDeletePanelCount ); +// m_iDeleteCount = m_iDeletePanelCount = 0; +#endif + + // this will generate all key and mouse events as well as make a real repaint + { + VPROF( "surface()->RunFrame()" ); + g_pSurface->RunFrame(); + } + + // give the system a chance to process + { + VPROF( "system()->RunFrame()" ); + g_pSystem->RunFrame(); + } + + // update cursor positions + if ( IsPC() && !IsReentrant() ) + { + VPROF( "update cursor positions" ); + int cursorX, cursorY; + g_pInput->GetCursorPosition(cursorX, cursorY); + + // this does the actual work given a x,y and a surface + g_pInput->UpdateMouseFocus(cursorX, cursorY); + + } + + if ( !bIsReentrant ) + { + VPROF( "input()->RunFrame()" ); + g_pInput->RunFrame(); + } + + // messenging + if ( !bIsReentrant ) + { + VPROF( "messaging" ); + + // send all the messages waiting in the queue + DispatchMessages(); + + // Do the OnTicks before purging messages, since in previous code they were posted after dispatch and wouldn't hit + // until next frame + int time = g_pSystem->GetTimeMillis(); + + m_bCanRemoveTickSignal = false; + + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Ticks", __FUNCTION__ ); + // directly invoke tick all who asked to be ticked + int count = m_TickSignalVec.Count(); + for (int i = count - 1; i >= 0; i-- ) + { + Tick_t *t = m_TickSignalVec[i]; + if ( t->bMarkDeleted ) + continue; + + if ( t->interval != 0 ) + { + if ( time < t->nexttick ) + continue; + + t->nexttick = time + t->interval; + } + + t->panel->Client()->OnTick(); + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Ticks: %s", __FUNCTION__, t->panel->Client()->GetName() ); + } + + m_bCanRemoveTickSignal = true; + + // get count again. panels could be added to tick vector in OnTick + count = m_TickSignalVec.Count(); + + // Remove all panels that tried to remove tick in OnTick + for (int i = count - 1; i >= 0; i-- ) + { + Tick_t *t = m_TickSignalVec[i]; + if ( t->bMarkDeleted ) + { + m_TickSignalVec.Remove( i ); + delete t; + } + } + } + + { + VPROF( "SolveTraverse" ); + // make sure the hierarchy is up to date + g_pSurface->SolveTraverse(g_pSurface->GetEmbeddedPanel()); + g_pSurface->ApplyChanges(); +#ifdef WIN32 + Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); +#endif + } + + if ( bIsReentrant ) + { + --m_nReentrancyCount; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CVGui::AllocPanel() +{ +#ifdef DEBUG + m_iDeleteCount++; +#endif + + VPanel *panel = new VPanel; + PanelCreated(panel); + return (VPANEL)panel; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVGui::FreePanel(VPANEL ipanel) +{ + PanelDeleted((VPanel *)ipanel); + delete (VPanel *)ipanel; +#ifdef DEBUG + m_iDeleteCount--; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the safe index of the panel +//----------------------------------------------------------------------------- +HPanel CVGui::PanelToHandle(VPANEL panel) +{ + if (panel) + return ((VPanel*)panel)->GetHPanel(); + return INVALID_PANEL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the panel at the specified index +//----------------------------------------------------------------------------- +VPANEL CVGui::HandleToPanel(HPanel index) +{ + if ( !m_HandleTable.IsHandleValid( index ) ) + { + return NULL; + } + return (VPANEL)m_HandleTable.GetHandle( (UtlHandle_t)index ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever a panel is constructed +//----------------------------------------------------------------------------- +void CVGui::PanelCreated(VPanel *panel) +{ + UtlHandle_t h = m_HandleTable.AddHandle(); + m_HandleTable.SetHandle( h, panel ); + +#if DUMP_PANEL_LIST + int nCount = m_HandleTable.GetHandleCount(); + int nActualCount = 0; + for ( int i = 0; i < nCount; ++i ) + { + UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i ); + if ( m_HandleTable.IsHandleValid( h ) ) + { + ++nActualCount; + } + } + + if ( nActualCount >= WARN_PANEL_NUMBER ) + { + FILE *file1 = fopen("panellist.txt", "w"); + if (file1 != NULL) + { + fprintf(file1, "Too many panels...listing them all.\n"); + int panelIndex; + for (panelIndex = 0; panelIndex < nCount; ++panelIndex) + { + UtlHandle_t h = m_HandleTable.GetHandleFromIndex( i ); + VPanel *pPanel = m_HandleTable.GetHandle( h ); + IClientPanel *ipanel = ( pPanel ) ? pPanel->Client() : NULL; + if ( ipanel ) + fprintf(file1, "panel %d: name: %s classname: %s\n", panelIndex, ipanel->GetName(), ipanel->GetClassName()); + else + fprintf(file1, "panel %d: can't get ipanel\n", panelIndex); + } + + fclose(file1); + } + } + + Assert( nActualCount < WARN_PANEL_NUMBER ); +#endif // DUMP_PANEL_LIST + + ((VPanel *)panel)->SetHPanel( h ); + + g_pSurface->AddPanel((VPANEL)panel); +} + +//----------------------------------------------------------------------------- +// Purpose: instantly stops the app from pointing to the focus'd object +// used when an object is being deleted +//----------------------------------------------------------------------------- +void CVGui::PanelDeleted(VPanel *focus) +{ + Assert( focus ); + g_pSurface->ReleasePanel((VPANEL)focus); + g_pInput->PanelDeleted((VPANEL)focus); + + // remove from safe handle list + UtlHandle_t h = ((VPanel *)focus)->GetHPanel(); + + Assert( m_HandleTable.IsHandleValid(h) ); + if ( m_HandleTable.IsHandleValid(h) ) + { + m_HandleTable.RemoveHandle( h ); + } + + ((VPanel *)focus)->SetHPanel( INVALID_PANEL ); + + // remove from tick signal dar + RemoveTickSignal( (VPANEL)focus ); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates or updates a tick signal for a panel. Returns NULL if already ticking. +//----------------------------------------------------------------------------- +CVGui::Tick_t* CVGui::CreateNewTick( VPANEL panel, int intervalMilliseconds ) +{ + Tick_t *t; + // See if it's already in list + int count = m_TickSignalVec.Count(); + for (int i = 0; i < count; i++ ) + { + Tick_t *t = m_TickSignalVec[i]; + if ( t->panel == (VPanel *)panel ) + { + // Go ahead and update intervals + t->interval = intervalMilliseconds; + t->nexttick = g_pSystem->GetTimeMillis() + t->interval; + + // Somebody added this panel back to the tick list, don't delete it + t->bMarkDeleted = false; + return NULL; + } + } + + // Add to list + t = new Tick_t; + + t->panel = (VPanel *)panel; + t->interval = intervalMilliseconds; + t->nexttick = g_pSystem->GetTimeMillis() + t->interval; + t->bMarkDeleted = false; + + if ( strlen( ((VPanel *)panel)->Client()->GetName() ) > 0 ) + { + strncpy( t->panelname, ((VPanel *)panel)->Client()->GetName(), sizeof( t->panelname ) ); + } + else + { + strncpy( t->panelname, ((VPanel *)panel)->Client()->GetClassName(), sizeof( t->panelname ) ); + } + + return t; +} + +//----------------------------------------------------------------------------- +// Purpose: Adds the panel to the tail of a tick signal list, so the panel receives a message every frame +//----------------------------------------------------------------------------- +void CVGui::AddTickSignal(VPANEL panel, int intervalMilliseconds /*=0*/ ) +{ + Tick_t* t = CreateNewTick( panel, intervalMilliseconds ); + + if ( t ) + { + // add the element to the end list + m_TickSignalVec.AddToTail( t ); + // panel is removed from list when deleted + } +} + +//----------------------------------------------------------------------------- +// Purpose: Adds the panel to the head of a tick signal list, so the panel receives a message every frame +//----------------------------------------------------------------------------- +void CVGui::AddTickSignalToHead(VPANEL panel, int intervalMilliseconds /*=0*/ ) +{ + Tick_t* t = CreateNewTick( panel, intervalMilliseconds ); + + if ( t ) + { + // simply add the element to the head list + m_TickSignalVec.AddToHead( t ); + // panel is removed from list when deleted + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVGui::RemoveTickSignal( VPANEL panel ) +{ + VPanel *search = (VPanel *)panel; + + // remove from tick signal dar + int count = m_TickSignalVec.Count(); + + for (int i = 0; i < count; i++ ) + { + Tick_t *tick = m_TickSignalVec[i]; + if ( tick->panel == search ) + { + if ( m_bCanRemoveTickSignal ) + { + m_TickSignalVec.Remove( i ); + delete tick; + } + else + { + tick->bMarkDeleted = true; + } + + return; + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: message pump +// loops through and sends all active messages +// note that more messages may be posted during the process +//----------------------------------------------------------------------------- +bool CVGui::DispatchMessages() +{ + int time = g_pSystem->GetTimeMillis(); + + m_InDispatcher = true; + bool doneWork = (m_MessageQueue.Count() > 12); + + bool bUsingDelayedQueue = (m_DelayedMessageQueue.Count() > 0); + + // Need two passes because we send the mouse move message after all + // other messages are done, but the mouse move message may itself generate + // some more messages + int nPassCount = 0; + while ( nPassCount < 2 ) + { + while (m_MessageQueue.Count() > 0 || (m_SecondaryQueue.Count() > 0) || bUsingDelayedQueue) + { + // get the first message + MessageItem_t *messageItem = NULL; + int messageIndex = 0; + + // use the secondary queue until it empties. empty it after each message in the + // primary queue. this makes primary messages completely resolve + bool bUsingSecondaryQueue = (m_SecondaryQueue.Count() > 0); + if (bUsingSecondaryQueue) + { + doneWork = true; + messageIndex = m_SecondaryQueue.Head(); + messageItem = &m_SecondaryQueue[messageIndex]; + } + else if (bUsingDelayedQueue) + { + if (m_DelayedMessageQueue.Count() >0) + { + messageItem = (MessageItem_t*)&m_DelayedMessageQueue.ElementAtHead(); + } + if (!messageItem || messageItem->_arrivalTime > time) + { + // no more items in the delayed message queue, move to the system queue + bUsingDelayedQueue = false; + continue; + } + } + else + { + messageIndex = m_MessageQueue.Head(); + messageItem = &m_MessageQueue[messageIndex]; + } + + // message debug code + + if ( m_bDebugMessages ) + { + const char *qname = bUsingSecondaryQueue ? "Secondary" : "Primary"; + + if (strcmp(messageItem->_params->GetName(), "Tick") + && strcmp(messageItem->_params->GetName(), "MouseFocusTicked") + && strcmp(messageItem->_params->GetName(), "KeyFocusTicked") + && strcmp(messageItem->_params->GetName(), "CursorMoved")) + { + if (!stricmp(messageItem->_params->GetName(), "command")) + { + g_pIVgui->DPrintf2( "%s Queue dispatching command( %s, %s -- %i )\n", qname, messageItem->_params->GetName(), messageItem->_params->GetString("command"), messageItem->_messageID ); + } + else + { + g_pIVgui->DPrintf2( "%s Queue dispatching( %s -- %i )\n", qname ,messageItem->_params->GetName(), messageItem->_messageID ); + } + } + } + + // send it + KeyValues *params = messageItem->_params; + + // Deal with special internal cursor movement messages + if ( messageItem->_messageTo == 0xFFFFFFFF ) + { + if ( !Q_stricmp( params->GetName(), "SetCursorPosInternal" ) ) + { + int nXPos = params->GetInt( "xpos", 0 ); + int nYPos = params->GetInt( "ypos", 0 ); + g_pInput->UpdateCursorPosInternal( nXPos, nYPos ); + } + } +#ifdef _X360 + else if ( messageItem->_messageTo == 0xFFFFFFFE ) // special tag to always give message to the active key focus + { + VPanel *vto = (VPanel *) g_pInput->GetCalculatedFocus(); + if (vto) + { + vto->SendMessage(params, g_pIVgui->HandleToPanel(messageItem->_from)); + } + } +#endif + else + { + VPanel *vto = (VPanel *)g_pIVgui->HandleToPanel(messageItem->_messageTo); + if (vto) + { + // Msg("Sending message: %s to %s\n", params ? params->GetName() : "\"\"", vto->GetName() ? vto->GetName() : "\"\""); + vto->SendMessage(params, g_pIVgui->HandleToPanel(messageItem->_from)); + } + } + + // free the keyvalues memory + // we can't reference the messageItem pointer anymore since the queue might have moved in memory + if (params) + { + params->deleteThis(); + } + + // remove it from the queue + if (bUsingSecondaryQueue) + { + m_SecondaryQueue.Remove(messageIndex); + } + else if (bUsingDelayedQueue) + { + m_DelayedMessageQueue.RemoveAtHead(); + } + else + { + m_MessageQueue.Remove(messageIndex); + } + } + + ++nPassCount; + if ( nPassCount == 1 ) + { + // Specifically post the current mouse position as a message + g_pInput->PostCursorMessage(); + } + } + + // Make sure the windows cursor is in the right place after processing input + // Needs to be done here because a message provoked by the cursor moved + // message may move the cursor also + g_pInput->HandleExplicitSetCursor( ); + + m_InDispatcher = false; + return doneWork; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVGui::MarkPanelForDeletion(VPANEL panel) +{ + PostMessage(panel, new KeyValues("Delete"), NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a message to the queue to be sent to a user +//----------------------------------------------------------------------------- +void CVGui::PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delay) +{ + // Ignore all messages in re-entrant mode + if ( IsReentrant() ) + { + Assert( 0 ); + if (params) + { + params->deleteThis(); + } + return; + } + + if (!target) + { + if (params) + { + params->deleteThis(); + } + return; + } + + MessageItem_t messageItem; + +#ifdef _X360 + // Special coded target that will always send the message to the key focus + // this is needed since we might send two messages on a tice, and the first + // could change the focus. + if( target == (VPANEL) MESSAGE_CURRENT_KEYFOCUS ) + { + messageItem._messageTo = 0xFFFFFFFE; + } + else +#endif + { + messageItem._messageTo = (target != (VPANEL) MESSAGE_CURSOR_POS ) ? g_pIVgui->PanelToHandle(target) : 0xFFFFFFFF; + } + messageItem._params = params; + Assert(params->GetName()); + messageItem._from = g_pIVgui->PanelToHandle(from); + messageItem._arrivalTime = 0; + messageItem._messageID = m_iCurrentMessageID++; + + /* message debug code + //if ( stricmp(messageItem._params->GetName(),"CursorMoved") && stricmp(messageItem._params->GetName(),"KeyFocusTicked")) + { + g_pIVgui->DPrintf2( "posting( %s -- %i )\n", messageItem._params->GetName(), messageItem._messageID ); + } + */ + + // add the message to the correct message queue + if (delay > 0.0f) + { + messageItem._arrivalTime = g_pSystem->GetTimeMillis() + (delay * 1000); + m_DelayedMessageQueue.Insert(messageItem); + } + else if (m_InDispatcher) + { + m_SecondaryQueue.AddToTail(messageItem); + } + else + { + m_MessageQueue.AddToTail(messageItem); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVGui::ShutdownMessage(unsigned int shutdownID) +{ + // broadcast Shutdown to all the top level windows, and see if any take notice + VPANEL panel = g_pSurface->GetEmbeddedPanel(); + for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) + { + g_pIVgui->PostMessage((VPANEL)((VPanel *)panel)->GetChild(i), new KeyValues("ShutdownRequest", "id", shutdownID), NULL); + } + + // post to the top level window as well + g_pIVgui->PostMessage(panel, new KeyValues("ShutdownRequest", "id", shutdownID), NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: Clears all the memory queues and free's their memory +//----------------------------------------------------------------------------- +void CVGui::ClearMessageQueues() +{ + Assert(!m_InDispatcher); + + {FOR_EACH_LL( m_MessageQueue, i ) + { + if (m_MessageQueue[i]._params) + { + m_MessageQueue[i]._params->deleteThis(); + } + }} + m_MessageQueue.RemoveAll(); + + // secondary message queue, holds all vgui messages generated by vgui + {FOR_EACH_LL( m_SecondaryQueue, i ) + { + if (m_SecondaryQueue[i]._params) + { + m_SecondaryQueue[i]._params->deleteThis(); + } + }} + m_SecondaryQueue.RemoveAll(); + + // timing queue, holds all the messages that have to arrive at a specified time + while (m_DelayedMessageQueue.Count() > 0) + { + if (m_DelayedMessageQueue.ElementAtHead()._params) + { + m_DelayedMessageQueue.ElementAtHead()._params->deleteThis(); + } + m_DelayedMessageQueue.RemoveAtHead(); + } +} + +/* +static void*(*staticMalloc)(size_t size)=malloc; +static void(*staticFree)(void* memblock)=free; + +static int g_iMemoryBlocksAllocated = 0; + +void *operator new(size_t size) +{ + g_iMemoryBlocksAllocated += 1; + return staticMalloc(size); +} + +void operator delete(void* memblock) +{ + if (!memblock) + return; + + g_iMemoryBlocksAllocated -= 1; + + if (g_iMemoryBlocksAllocated < 0) + { + int x = 3; + } + + staticFree(memblock); +} + +void *operator new [] (size_t size) +{ + return staticMalloc(size); +} + +void operator delete [] (void *pMem) +{ + staticFree(pMem); +} +*/ + +void CVGui::DPrintf(const char* format,...) +{ + char buf[2048]; + va_list argList; + + va_start(argList,format); + Q_vsnprintf(buf,sizeof( buf ), format,argList); + va_end(argList); + +#ifdef WIN32 + ::OutputDebugString(buf); +#else + Msg( "%s", buf ); +#endif +} + +void CVGui::DPrintf2(const char* format,...) +{ + char buf[2048]; + va_list argList; + static int ctr=0; + + Q_snprintf(buf,sizeof( buf ), "%d:",ctr++ ); + + va_start(argList,format); + Q_vsnprintf(buf+strlen(buf),sizeof( buf )-strlen(buf),format,argList); + va_end(argList); + +#ifdef WIN32 + ::OutputDebugString(buf); +#else + Msg( "%s", buf ); +#endif +} + +void vgui::vgui_strcpy(char* dst,int dstLen,const char* src) +{ + Assert(dst!=null); + Assert(dstLen>=0); + Assert(src!=null); + + int srcLen=strlen(src)+1; + if(srcLen>dstLen) + { + srcLen=dstLen; + } + + memcpy(dst,src,srcLen-1); + dst[srcLen-1]=0; +} + +//----------------------------------------------------------------------------- + // HL2/TFC specific stuff +//----------------------------------------------------------------------------- +// Here's where the app systems get to learn about each other +//----------------------------------------------------------------------------- +bool CVGui::Connect( CreateInterfaceFn factory ) +{ + if ( !BaseClass::Connect( factory ) ) + return false; + + if ( !g_pFullFileSystem || !g_pVGuiLocalize ) + { + Warning( "IVGui unable to connect to required interfaces!\n" ); + return false; + } + + return VGui_InternalLoadInterfaces( &factory, 1 ); +} + +void CVGui::Disconnect() +{ + // FIXME: Blat out interface pointers + BaseClass::Disconnect(); +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +InitReturnVal_t CVGui::Init() +{ + m_hContext = DEFAULT_VGUI_CONTEXT; + m_bDebugMessages = CommandLine()->FindParm( "-vguimessages" ) ? true : false; + + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + return INIT_OK; +} + +void CVGui::Shutdown() +{ + g_pSystem->SaveUserConfigFile(); + + DestroyAllContexts(); + ClearMessageQueues(); + + g_pSystem->Shutdown(); + g_pScheme->Shutdown(true); + + if ( !g_pSurface->QueryInterface( MAT_SYSTEM_SURFACE_INTERFACE_VERSION ) ) + { + g_pSurface->Shutdown(); + } + + BaseClass::Shutdown(); +} + +//----------------------------------------------------------------------------- +// Here's where systems can access other interfaces implemented by this object +// Returns NULL if it doesn't implement the requested interface +//----------------------------------------------------------------------------- +void *CVGui::QueryInterface( const char *pInterfaceName ) +{ + // FIXME: Should this go here? + // Access other global interfaces exposed by this system... + CreateInterfaceFn vguiFactory = Sys_GetFactoryThis(); + return vguiFactory( pInterfaceName, NULL ); +} diff --git a/vgui2/src/vgui_dll.vpc b/vgui2/src/vgui_dll.vpc new file mode 100644 index 0000000..e92dea6 --- /dev/null +++ b/vgui2/src/vgui_dll.vpc @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// VGUI_DLL.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" +$Macro OUTBINNAME "vgui2" + +$include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;..\include" + $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty" + $PreprocessorDefinitions "$BASE;DONT_PROTECT_FILEIO_FUNCTIONS" +// $TreatWchar_tAsBuiltinType "No" + } + + $Linker + { + $AdditionalDependencies "$BASE Imm32.lib Shlwapi.lib odbc32.lib odbccp32.lib winmm.lib" [$WIN32] + $SystemLibraries "iconv" [$OSXALL] //||$LINUXALL] + $SystemFrameworks "Carbon" [$OSXALL] + } +} + +$Project "vgui2" +{ + $Folder "Source Files" + { + $File "Bitmap.cpp" + $File "Border.cpp" + $File "ScalableImageBorder.cpp" + $File "ImageBorder.cpp" + $File "fileimage.cpp" + $File "$SRCDIR\public\filesystem_helpers.cpp" + $File "$SRCDIR\public\filesystem_init.cpp" + $File "InputWin32.cpp" + $File "LocalizedStringTable.cpp" + $File "MemoryBitmap.cpp" + $File "Memorybitmap.h" + $File "MessageListener.cpp" + $File "Scheme.cpp" + $File "Surface.cpp" [$WIN32] + $File "System.cpp" [$WINDOWS||$X360] + $File "system_posix.cpp" [$POSIX] + $File "$SRCDIR\public\UnicodeFileHelpers.cpp" + $File "vgui.cpp" + $File "vgui_internal.cpp" + $File "vgui_key_translation.cpp" + $File "VPanel.cpp" + $File "VPanelWrapper.cpp" + $File "keyrepeat.cpp" + } + + $Folder "Header Files" + { + $File "bitmap.h" + $File "fileimage.h" + $File "IMessageListener.h" + $File "vgui_internal.h" + $File "vgui_key_translation.h" + $File "VPanel.h" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\tier0\basetypes.h" + $File "$SRCDIR\public\Color.h" + $File "$SRCDIR\public\vgui\Cursor.h" + $File "$SRCDIR\public\filesystem.h" + $File "$SRCDIR\common\vgui_surfacelib\FontAmalgam.h" + $File "$SRCDIR\common\vgui_surfacelib\FontManager.h" + $File "$SRCDIR\public\tier1\interface.h" + $File "$SRCDIR\public\vgui\KeyCode.h" + $File "$SRCDIR\common\SteamBootStrapper.h" + $File "$SRCDIR\public\tier1\strtools.h" + $File "$SRCDIR\public\UnicodeFileHelpers.h" + $File "$SRCDIR\public\tier1\utlbuffer.h" + $File "$SRCDIR\public\tier1\utllinkedlist.h" + $File "$SRCDIR\public\tier1\utlmemory.h" + $File "$SRCDIR\public\tier1\utlpriorityqueue.h" + $File "$SRCDIR\public\tier1\utlrbtree.h" + $File "$SRCDIR\public\tier1\utlvector.h" + $File "$SRCDIR\public\mathlib\vector2d.h" + $File "$SRCDIR\public\vgui\VGUI.h" + $File "$SRCDIR\public\vstdlib\vstdlib.h" + $File "$SRCDIR\common\vgui_surfacelib\Win32Font.h" + $File "$SRCDIR\public\vgui\KeyRepeat.h" + } + + $Folder "Interfaces" + { + $File "$SRCDIR\public\appframework\IAppSystem.h" + $File "$SRCDIR\public\vgui\IBorder.h" + $File "$SRCDIR\public\vgui\IClientPanel.h" + $File "$SRCDIR\public\vgui\IHTML.h" + $File "$SRCDIR\public\vgui\IImage.h" + $File "$SRCDIR\public\vgui\IInput.h" + $File "$SRCDIR\public\vgui\ILocalize.h" + $File "$SRCDIR\public\vgui\IPanel.h" + $File "$SRCDIR\public\vgui\IScheme.h" + $File "$SRCDIR\public\vgui\ISurface.h" + $File "$SRCDIR\public\vgui\ISystem.h" + $File "$SRCDIR\public\vgui\IVGui.h" + $File "$SRCDIR\public\vgui\IVguiMatInfo.h" + $File "$SRCDIR\public\vgui\IVguiMatInfoVar.h" + $File "VGUI_Border.h" + $File "ScalableImageBorder.h" + $File "ImageBorder.h" + } + + $Folder "Link Libraries" + { + $Lib vgui_surfacelib + $Lib tier2 + $Lib tier3 + $ImpLib SDL2 [$SDL] + } + +} diff --git a/vgui2/src/vgui_internal.cpp b/vgui2/src/vgui_internal.cpp new file mode 100644 index 0000000..ffcc63a --- /dev/null +++ b/vgui2/src/vgui_internal.cpp @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Core implementation of vgui +// +// $NoKeywords: $ +//=============================================================================// + +#include "vgui_internal.h" + +#include <vgui/ISurface.h> +#include <vgui/ILocalize.h> +#include <vgui/IPanel.h> +#include "filesystem.h" +#include <vstdlib/IKeyValuesSystem.h> + +#include <stdio.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +namespace vgui +{ + +ISurface *g_pSurface = NULL; +IPanel *g_pIPanel = NULL; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void *InitializeInterface( char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories ) +{ + void *retval; + + for ( int i = 0; i < numFactories; i++ ) + { + CreateInterfaceFn factory = factoryList[ i ]; + if ( !factory ) + continue; + + retval = factory( interfaceName, NULL ); + if ( retval ) + return retval; + } + + // No provider for requested interface!!! + // assert( !"No provider for requested interface!!!" ); + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool VGui_InternalLoadInterfaces( CreateInterfaceFn *factoryList, int numFactories ) +{ + // loads all the interfaces + g_pSurface = (ISurface *)InitializeInterface(VGUI_SURFACE_INTERFACE_VERSION, factoryList, numFactories ); +// g_pKeyValues = (IKeyValues *)InitializeInterface(KEYVALUES_INTERFACE_VERSION, factoryList, numFactories ); + g_pIPanel = (IPanel *)InitializeInterface(VGUI_PANEL_INTERFACE_VERSION, factoryList, numFactories ); + + if (g_pSurface && /*g_pKeyValues &&*/ g_pIPanel) + return true; + + return false; +} + +} // namespace vgui diff --git a/vgui2/src/vgui_internal.h b/vgui2/src/vgui_internal.h new file mode 100644 index 0000000..65d2bfb --- /dev/null +++ b/vgui2/src/vgui_internal.h @@ -0,0 +1,50 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Wraps pointers to basic vgui interfaces +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VGUI_INTERNAL_H +#define VGUI_INTERNAL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/VGUI.h> +#include "interface.h" +#include "tier3/tier3.h" +#include "xbox/xboxstubs.h" + +namespace vgui +{ + +bool VGui_InternalLoadInterfaces( CreateInterfaceFn *factoryList, int numFactories ); + +// <vgui/IInputInternal.h> header +extern class IInputInternal *g_pInput; + +// <vgui/IScheme.h> header +extern class ISchemeManager *g_pScheme; + +// <vgui/ISurface.h> header +extern class ISurface *g_pSurface; + +// <vgui/ISystem.h> header +extern class ISystem *g_pSystem; + +// <vgui/IVGui.h> header +extern class IVGui *g_pIVgui; + +// <vgui/IPanel.h> header +extern class IPanel *g_pIPanel; + +// methods +void vgui_strcpy(char *dst, int dstLen, const char *src); +} // namespace vgui + + + + +#endif // VGUI_INTERNAL_H diff --git a/vgui2/src/vgui_key_translation.cpp b/vgui2/src/vgui_key_translation.cpp new file mode 100644 index 0000000..0377a5d --- /dev/null +++ b/vgui2/src/vgui_key_translation.cpp @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#if defined( WIN32 ) && !defined( _X360 ) +#include <wtypes.h> +#include <winuser.h> +#include "xbox/xboxstubs.h" +#endif +#include "tier0/dbg.h" +#include "vgui_key_translation.h" +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif +#ifdef POSIX +#define VK_RETURN -1 +#endif + +#include "tier2/tier2.h" +#include "inputsystem/iinputsystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +vgui::KeyCode KeyCode_VirtualKeyToVGUI( int key ) +{ + // Some tools load vgui for localization and never use input + if ( !g_pInputSystem ) + return KEY_NONE; + return g_pInputSystem->VirtualKeyToButtonCode( key ); +} + +int KeyCode_VGUIToVirtualKey( vgui::KeyCode code ) +{ + // Some tools load vgui for localization and never use input + if ( !g_pInputSystem ) + return VK_RETURN; + + return g_pInputSystem->ButtonCodeToVirtualKey( code ); +} diff --git a/vgui2/src/vgui_key_translation.h b/vgui2/src/vgui_key_translation.h new file mode 100644 index 0000000..3d08435 --- /dev/null +++ b/vgui2/src/vgui_key_translation.h @@ -0,0 +1,20 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef VGUI_KEY_TRANSLATION_H +#define VGUI_KEY_TRANSLATION_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui/KeyCode.h> + +// Convert from Windows scan codes to VGUI key codes. +vgui::KeyCode KeyCode_VirtualKeyToVGUI( int key ); +int KeyCode_VGUIToVirtualKey( vgui::KeyCode keycode ); + + +#endif // VGUI_KEY_TRANSLATION_H diff --git a/vgui2/src/xbox/xbox.def b/vgui2/src/xbox/xbox.def new file mode 100644 index 0000000..6ccbe24 --- /dev/null +++ b/vgui2/src/xbox/xbox.def @@ -0,0 +1,3 @@ +LIBRARY vgui2_360.dll +EXPORTS + CreateInterface @1 |