Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / C++

AutoText Add-in for Visual Studio 6.0

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
3 Mar 2001 115.9K   1.5K   39  
Adds the auto text feature from MS Word (tm) to the Developer Studio (tm)
// DSAddIn.cpp : header file
//
// Written by Christoph Weber (cpwech@oopsi.de)
// Copyright (c) 2001.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. If 
// the source code in this file is used in any commercial application 
// then a simple email would be nice.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to your computer.
//
// Please let me know of any bugs/mods/improvements that you have found/fixed
// and I will fix/incorporate them into this file. 
//

#include "stdafx.h"
#include "DeveloperStudioAutoText.h"
#include "DSAddIn.h"
#include "Commands.h"
#include "ObjModel\TextGuid.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


CDSAddIn::CDSAddIn() : m_wndDevStudioMainWindow( &m_wndActiveMdiChild )
{
}

CDSAddIn::~CDSAddIn()
{
}

// This is called when the user first loads the add-in, and on start-up
//  of each subsequent Developer Studio session
STDMETHODIMP CDSAddIn::OnConnection(IApplication* pApp, VARIANT_BOOL bFirstTime,
		long dwCookie, VARIANT_BOOL* OnConnection)
{
   HWND  hWnd;
   HWND  hDesktopWnd = ::GetDesktopWindow();
   char  szWindowTitle[512];

	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	
	// Store info passed to us
	IApplication* pApplication = NULL;
	if (FAILED(pApp->QueryInterface(IID_IApplication, (void**) &pApplication))
		|| pApplication == NULL)
	{
		*OnConnection = VARIANT_FALSE;
		return S_OK;
	}

	m_dwCookie = dwCookie;

	// Create command dispatch, send info back to DevStudio
	CCommandsObj::CreateInstance(&m_pCommands);
	m_pCommands->AddRef();

   //First find the main window
   hWnd = ::GetActiveWindow();
   while( hWnd  &&  hWnd != hDesktopWnd )
   {
       m_hDevStudioWnd = hWnd;
       hWnd = ::GetParent(hWnd);
   }

   GetWindowText( m_hDevStudioWnd, szWindowTitle, 512 );
   
   if( strstr( szWindowTitle, "Microsoft Visual C++" ) )
   {
      m_wndDevStudioMainWindow.SubclassWindow( m_hDevStudioWnd );

      // The MDIArea always has ID 0xe900
      m_hDevStudioMDIArea = GetDlgItem( m_hDevStudioWnd, 0xe900 );

      m_wndMDIAreaManager.SubclassWindow( m_hDevStudioMDIArea );
   }
   else
   {
      ASSERT( FALSE );
      MessageBox( NULL, "Main window title not matching!\nInstallation failed.", "WARNING!", MB_OK|MB_ICONWARNING );
   }

   m_wndActiveMdiChild.SetCommandObject( m_pCommands );
   m_pCommands->SetAddIn( this );

	// The QueryInterface above AddRef'd the Application object.  It will
	//  be Release'd in CCommand's destructor.
	m_pCommands->SetApplicationObject(pApplication);

	// (see stdafx.h for the definition of VERIFY_OK)

	VERIFY_OK(pApplication->SetAddInInfo((long) AfxGetInstanceHandle(),
		(LPDISPATCH) m_pCommands, IDR_TOOLBAR_MEDIUM, IDR_TOOLBAR_LARGE, m_dwCookie));

	// Inform DevStudio of the commands we implement

	// TODO: Replace the AddCommand call below with a series of calls,
	//  one for each command your add-in will add.

	// The command name should not be localized to other languages.  The 
	//  tooltip, command description, and other strings related to this
	//  command are stored in the string table (IDS_CMD_STRING) and should
	//  be localized.
	LPCTSTR szCommand = _T("DeveloperStudioAutoTextCommand");
	VARIANT_BOOL bRet;
	CString strCmdString;
	strCmdString.LoadString(IDS_CMD_STRING);
	strCmdString = szCommand + strCmdString;
	CComBSTR bszCmdString(strCmdString);
	CComBSTR bszMethod(_T("DeveloperStudioAutoTextCommandMethod"));
	CComBSTR bszCmdName(szCommand);
	VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_dwCookie, &bRet));
	if (bRet == VARIANT_FALSE)
	{
		// AddCommand failed because a command with this name already
		//  exists.  You may try adding your command under a different name.
		//  Or, you can fail to load as we will do here.
		*OnConnection = VARIANT_FALSE;
		return S_OK;
	}

	// Add toolbar buttons only if this is the first time the add-in
	//  is being loaded.  Toolbar buttons are automatically remembered
	//  by Developer Studio from session to session, so we should only
	//  add the toolbar buttons once.
	if (bFirstTime == VARIANT_TRUE)
	{
		VERIFY_OK(pApplication->
			AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));
	}

   if( bRet )
   {
      /////
	   szCommand = _T("DeveloperStudioOpenHeaderCommand");
	   CString strCmdString;
	   strCmdString.LoadString(IDS_OPEN_HEADER_CMD_STRING);
	   strCmdString = szCommand + strCmdString;
	   bszCmdString = strCmdString;
	   bszMethod =_T("DeveloperStudioOpenHeaderCommandMethod");
	   bszCmdName =szCommand;
	   VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 1, m_dwCookie, &bRet));
	   if (bRet == VARIANT_FALSE)
	   {
		   // AddCommand failed because a command with this name already
		   //  exists.  You may try adding your command under a different name.
		   //  Or, you can fail to load as we will do here.
		   *OnConnection = VARIANT_FALSE;
		   return S_OK;
	   }

	   // Add toolbar buttons only if this is the first time the add-in
	   //  is being loaded.  Toolbar buttons are automatically remembered
	   //  by Developer Studio from session to session, so we should only
	   //  add the toolbar buttons once.
	   if (bFirstTime == VARIANT_TRUE)
	   {
		   VERIFY_OK(pApplication->
			   AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));
	   }
   }

	*OnConnection = VARIANT_TRUE;
	return S_OK;
}

// This is called on shut-down, and also when the user unloads the add-in
STDMETHODIMP CDSAddIn::OnDisconnection(VARIANT_BOOL bLastTime)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_pCommands->UnadviseFromEvents();
	m_pCommands->Release();
	m_pCommands = NULL;

   //disconnect our subclassed windows. We don't need them anymore
   if( m_wndActiveMdiChild.m_hWnd )
   {
      m_wndActiveMdiChild.UnsubclassWindow();
   }

   if( m_wndDevStudioMainWindow.m_hWnd )
   {
      m_wndDevStudioMainWindow.UnsubclassWindow();
   }

   if( m_wndMDIAreaManager.m_hWnd )
   {
      m_wndMDIAreaManager.UnsubclassWindow();
   }

	return S_OK;
}


void CDSAddIn::SetMDIWindowActive( IDispatch* pITheWindow, IDispatch* pIDocument, BOOL bActivate )
{
   CMDIChildWnd*  pMdi = m_wndMDIAreaManager.GetActiveMDI();
   
   m_wndActiveMdiChild.SetActive( pMdi, pITheWindow, pIDocument );
}






//Special hack, because cursor movements and some other special keys
//are translated by the mainframe into WM_COMMAND-messages
BEGIN_MESSAGE_MAP( C_DevStudioMainWindowFilter, CWnd)
   ON_COMMAND_RANGE( 0x18808, 0x1880b, OnKeyLeftRightUpDown )
   ON_COMMAND( 0x18781, OnEnterPressed )
   ON_COMMAND( 0x18763, OnTabPressed )
   ON_COMMAND( 0x1878b, OnBackspacePressed )
   ON_COMMAND( 0x1e120, OnDelPressed )
   ON_COMMAND( ID_EDIT_UNDO, OnUndo )
END_MESSAGE_MAP()

C_DevStudioMainWindowFilter::C_DevStudioMainWindowFilter( C_ActiveMDIWindowFilter* pActiveMdiWindow )
{
   m_pActiveMdiWindow = pActiveMdiWindow;
}

C_DevStudioMainWindowFilter::~C_DevStudioMainWindowFilter()
{
   if( m_hWnd )
   {
      UnsubclassWindow();
   }
}



//route these to the active MDI window

LRESULT C_DevStudioMainWindowFilter::OnKeyLeftRightUpDown( UINT nMsg )
{
   m_pActiveMdiWindow->OnKeyLeftRightUpDown( nMsg-0x8808 ); //left=0,right=1
   return CWnd::Default();
}

void C_DevStudioMainWindowFilter::OnEnterPressed()
{
   m_pActiveMdiWindow->OnSpecialKeyPressed( VK_RETURN );
   CWnd::Default();
}

void C_DevStudioMainWindowFilter::OnTabPressed()
{
   m_pActiveMdiWindow->OnSpecialKeyPressed( VK_TAB );
   CWnd::Default();
}

void C_DevStudioMainWindowFilter::OnBackspacePressed()
{
   m_pActiveMdiWindow->OnSpecialKeyPressed( VK_BACK );
   CWnd::Default();
}

void C_DevStudioMainWindowFilter::OnDelPressed()
{
   m_pActiveMdiWindow->OnSpecialKeyPressed( VK_DELETE );
   CWnd::Default();
}

void C_DevStudioMainWindowFilter::OnUndo()
{
   m_pActiveMdiWindow->OnSpecialKeyPressed( ID_EDIT_UNDO );
   CWnd::Default();
}





//We also subclass the mdiclientarea, so we easily can get the active
//MDIChildFrame and to determine when to replace text
C_MDIAreaManagerFilter::C_MDIAreaManagerFilter()
{
}

C_MDIAreaManagerFilter::~C_MDIAreaManagerFilter()
{
   if( m_hWnd )
   {
      UnsubclassWindow();
   }
}

CMDIChildWnd* C_MDIAreaManagerFilter::GetActiveMDI()
{
   CMDIChildWnd*  pErg;
   BOOL           bMaximized;

	HWND hWnd = (HWND)::SendMessage(m_hWnd, WM_MDIGETACTIVE, 0, (LPARAM)&bMaximized);
	
   pErg = (CMDIChildWnd*)CWnd::FromHandle(hWnd);

   return pErg;
}




//Finally we subclass the active MDIWindow's view to geht all
//Keyboard-Messages
BEGIN_MESSAGE_MAP(C_ActiveMDIWindowFilter, CWnd)
   ON_WM_KEYDOWN()
   ON_WM_CHAR()
   ON_COMMAND( ID_DONT_CORRECT_IT, OnDummy )
   ON_COMMAND( ID_REPLACE_BY, OnDummy )
END_MESSAGE_MAP()

C_ActiveMDIWindowFilter::C_ActiveMDIWindowFilter()
{
   m_pIActiveTextDocument = NULL;
   m_bEnableAutoCorrect = TRUE;
}

C_ActiveMDIWindowFilter::~C_ActiveMDIWindowFilter()
{
   if( m_hWnd )
   {
      UnsubclassWindow();
   }

   if( m_pIActiveTextDocument )
   {
      m_pIActiveTextDocument->Release();
   }
}

void C_ActiveMDIWindowFilter::SetCommandObject( CCommands* pCommands )
{
   m_pCommands = pCommands;
}

void C_ActiveMDIWindowFilter::SetActive( CWnd* pActiveMDIWindow, IDispatch* pWindow, IDispatch* pIDocument )
{
   if( m_hWnd )
   {
      UnsubclassWindow();
   }

   if( m_pIActiveTextDocument )
   {
      m_pIActiveTextDocument->Release();
      m_pIActiveTextDocument = NULL;
   }

   if( pActiveMDIWindow && pIDocument )
   {
      pActiveMDIWindow = pActiveMDIWindow->GetDlgItem( 0xe900 ); //View-Frame
      if( pActiveMDIWindow )
      {
         pActiveMDIWindow = pActiveMDIWindow->GetDlgItem( 0xe900 ); //eigentlicher View
      }

      if( pActiveMDIWindow )
      {
         if( SUCCEEDED( pIDocument->QueryInterface( IID_ITextDocument, (void**)&m_pIActiveTextDocument ) ) )
         {
            SubclassWindow( pActiveMDIWindow->GetSafeHwnd() );
         }
      }//endif pActiveMDIWindow
   }
}

void C_ActiveMDIWindowFilter::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
   OnSpecialKeyPressed( nChar );
   CWnd::OnKeyDown( nChar,nRepCnt,nFlags );
}

void C_ActiveMDIWindowFilter::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags )
{
   OnSpecialKeyPressed( 0x0100 | nChar );
   CWnd::OnChar( nChar,nRepCnt,nFlags );
}

void C_ActiveMDIWindowFilter::OnDummy()
{
}

void C_ActiveMDIWindowFilter::OnSpecialKeyPressed( UINT nKey )
{
   IDispatch*     pSelection;
   CMenu          mnuMenu;
   UINT           uID = 0;
   CWnd*          pWnd;
   CString        strNewText;
   CPoint         ptCaretPos;

   try
   {
      if( m_pIActiveTextDocument && m_pCommands->m_bEnableSystem )
      {
         if( nKey==0x10 )
         {
            return;///SHIFT
         }

         switch( nKey )
         {
            case 0x0100 | ' ':
            case 0x0100 | ',':
            case 0x0100 | ';':
            case 0x0100 | '(':
            case VK_TAB:
            case VK_RETURN:

               if( SUCCEEDED( m_pIActiveTextDocument->get_Selection( &pSelection ) ) )
               {
                  if( !m_bEnableAutoCorrect )
                  {
                     //Do we have a replacement text?
                     strNewText = CheckAutoText( pSelection, FALSE );
                     if( !strNewText.IsEmpty() )
                     {
                        //yes we have a new text
                        mnuMenu.LoadMenu( IDR_AUTO_CORRECT_CONTEXTMENU );
               
                        pWnd = this;
                        if( pWnd )
                        {
                           ::GetCaretPos( &ptCaretPos );

                           ClientToScreen( &ptCaretPos );

                           ptCaretPos.x += 8; //etwas abstand halten

                           SetMenuDefaultItem( mnuMenu.GetSubMenu(0)->m_hMenu, ID_REPLACE_BY, FALSE );
                           mnuMenu.GetSubMenu(0)->ModifyMenu( ID_REPLACE_BY, MF_BYCOMMAND|MF_STRING|MF_ENABLED, ID_REPLACE_BY, 
                                                            "Replace with: " + strNewText );

                           //Select first menu item trough faking a cursor down (keydown and up)
                           PostMessage( WM_KEYDOWN, VK_DOWN, 0 );
                           PostMessage( WM_KEYUP, VK_DOWN, 0 );

                           uID = TrackPopupMenu( mnuMenu.GetSubMenu(0)->m_hMenu, TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY, 
                              ptCaretPos.x, ptCaretPos.y, NULL, pWnd->m_hWnd, NULL );
                        }

                        switch( uID )
                        {
                           case 0:
                           case ID_DONT_CORRECT_IT:
                           break;

                           case ID_REPLACE_BY:
                              m_bEnableAutoCorrect = TRUE;
                           break;
                        }
                     }//endif !strNewText.IsEmpty()
                  }//endif( !m_bEnableAutoCorrect )

                  if( m_bEnableAutoCorrect )
                  {
                     CheckAutoText( pSelection, TRUE );
                  }

                  //release selection object
                  pSelection->Release();
               }

               //In future do it again
               m_bEnableAutoCorrect = TRUE;
            break;

            case VK_DELETE:
            case VK_BACK:
               //temporarily disable auto replacing
               m_bEnableAutoCorrect = FALSE;
            break;

            case ID_EDIT_UNDO:
               //temporarily disable auto replacing
               m_bEnableAutoCorrect = FALSE;
            break;

            default:
            break;
         }
      }//endif m_pIDispTextWindow
   }
   catch( ... )
   {
      TRACE( "Unhandled exception in OnSpecialKeyPressed\n" );
   }
}

void C_ActiveMDIWindowFilter::OnKeyLeftRightUpDown( UINT nMoveCmd )
{
   switch( nMoveCmd )
   {
      case 0: //left
      case 1: //right
      case 2: //up
      case 3: //down
         //after cursor  moves allow auto correction again
         m_bEnableAutoCorrect = TRUE;
      break;
   }
}

//If bAutoReplace is TRUE the text will immediately be replaced if possible. 
//Otherwise only the replacement text (if available) will be returned. 
//If there is nothing replaceable type by the user the return value will 
//be empty and the text won't be changed.
CString C_ActiveMDIWindowFilter::CheckAutoText( IDispatch*  pIDispSelection, BOOL bAutoReplace )
{
   ITextSelection*   pSelection;
   long              lFirstLine;
   long              lStartColumn;
   long              lLastLine;
   BSTR              bstrText = NULL;
   CString           strNewText;
   COleVariant       varDsExtend( long(1), VT_I4 );
   COleVariant       varDsMove( long(0), VT_I4 );
   COleVariant       varEmpty( DISP_E_PARAMNOTFOUND, VT_ERROR );
   CString           strErg;
   CString           strText;

   //get selection from text editor
   if( SUCCEEDED( pIDispSelection->QueryInterface( IID_ITextSelection, (void**)&pSelection ) ) )
   {
      //remember position because we need to change it
      pSelection->get_CurrentLine( &lFirstLine );
      pSelection->get_BottomLine( &lLastLine );
      pSelection->get_CurrentColumn( &lStartColumn );
      pSelection->get_Text( &bstrText );
      strText = bstrText;
      SysFreeString( bstrText );

      //only try to replace if selection start and end line are the same
      //and the text is at least 2 chars long
      if( lFirstLine == lLastLine && 
          strText.GetLength() < 2 )
      {
         //select previous typed word
         pSelection->WordLeft( varDsExtend, COleVariant( 1L, VT_I4 ) );
         //and get the text of it
         pSelection->get_Text( &bstrText );

         //I prefer CStrings... remeber last typed word
         m_pCommands->m_strLastTypedWord = bstrText;
         SysFreeString( bstrText );

         //try to find the word
         m_pCommands->m_strmapAutoText.Lookup( m_pCommands->m_strLastTypedWord, strNewText );

         //Better suggestion from file?
         if( !strNewText.IsEmpty() )
         {
            strErg = strNewText;

            //Yes! Now check if we only want to know or to really do it.
            if( bAutoReplace )
            {
               //Replace last word
               bstrText = strNewText.AllocSysString();
               pSelection->put_Text( bstrText );
               SysFreeString( bstrText );

               //set cursor behind new text if it's longer
               lStartColumn+= strNewText.GetLength() - m_pCommands->m_strLastTypedWord.GetLength();
            }
         }

         //Back to the position we started
         pSelection->MoveTo( lFirstLine, lStartColumn, varDsMove );
      }

      //cleanup
      pSelection->Release();
   }

   return strErg;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions