//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //===========================================================================// #if defined( WIN32 ) && !defined( _X360 ) #include #include #define DO_IME #endif #include #include "vgui_internal.h" #include "VPanel.h" #include "utlvector.h" #include #include "tier0/vcrmode.h" #include #include #include #include #include #include #include #include #include #include "vgui/Cursor.h" #include #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] =",_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; }