// WindowsListDialog.cpp : implementation file
//
#include "stdafx.h"
#include "WindowsSwitcher.h"
#include "WindowsListDialog.h"
#include "KeyBindingsDialog.h"
#include "AboutDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CWindowsListDialog dialog
CString CWindowsListDialog::m_oActiveProjectNameString;
CWindowsListDialog::CWindowsListDialog( IApplication* pIApplication, CWnd* pParent /*=NULL*/ )
: CDialog(CWindowsListDialog::IDD, pParent), m_pIApplication( pIApplication )
{
//{{AFX_DATA_INIT(CWindowsListDialog)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
CWindowsListDialog::~CWindowsListDialog()
{
DeleteWindowsDescriptions();
}
void CWindowsListDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CWindowsListDialog)
DDX_Control(pDX, IDC_EDIT_FILTER, m_oFilterEdit);
DDX_Control(pDX, IDC_LIST_WINDOWS, m_oWindowsListCtrl);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CWindowsListDialog, CDialog)
//{{AFX_MSG_MAP(CWindowsListDialog)
ON_EN_CHANGE(IDC_EDIT_FILTER, OnChangeEditFilter)
ON_BN_CLICKED(IDC_BUTTON_ACVITATE, OnButtonAcvitate)
ON_BN_CLICKED(IDC_BUTTON_CLOSE_WND, OnButtonCloseWnd)
ON_NOTIFY(NM_DBLCLK, IDC_LIST_WINDOWS, OnDblclkListWindows)
ON_WM_DESTROY()
ON_WM_CONTEXTMENU()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWindowsListDialog message handlers
BOOL CWindowsListDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// change style
DWORD dwStyle = 0;
m_oWindowsListCtrl.GetExtendedStyle();
dwStyle |= LVS_EX_FULLROWSELECT;
m_oWindowsListCtrl.SetExtendedStyle( dwStyle );
m_oWindowsListCtrl.InsertColumn( eColumnIndexContent, "Content", LVCFMT_LEFT, 200 );
m_oWindowsListCtrl.InsertColumn( eColumnIndexProject, "Project", LVCFMT_LEFT, 150 );
m_oWindowsListCtrl.InsertColumn( eColumnIndexFullPath, "Full path", LVCFMT_LEFT, 500 );
LoadDialogSettings();
try
{
m_pIProjects = m_pIApplication->Projects;
m_pIWindows = m_pIApplication->Windows;
try
{
::DSSharedObjects::IGenericProjectPtr pIActiveProject = m_pIApplication->ActiveProject;
pIActiveProject = m_pIApplication->ActiveProject;
m_oActiveProjectNameString = (CComBSTR)(BSTR)pIActiveProject->Name;
}
catch( _com_error & ) // don't generate an error if none project is available
{
}
ReadProjectNames();
GetWindowsDescriptions();
PopulateWindowsListCtrl();
}
catch( _com_error &oComError )
{
AfxMessageBox( oComError.Description() );
return FALSE;
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
int __cdecl CompareProjectByPathLength( const void *elem1, const void *elem2 )
{
CProject *poProject1 = (CProject *)elem1;
CProject *poProject2 = (CProject *)elem2;
return poProject2->m_oPathString.GetLength() - poProject1->m_oPathString.GetLength();
}
void CWindowsListDialog::ReadProjectNames()
{
m_aoProjectsArray.SetSize( m_pIProjects->Count );
for( int iIndex = 1; iIndex <= m_pIProjects->Count; iIndex++ )
{
CString oFullNameString = (BSTR) m_pIProjects->Item( CComVariant( iIndex ) )->FullName;
int iLastBackSlashIndex = oFullNameString.ReverseFind( '\\' );
m_aoProjectsArray[iIndex - 1].m_oNameString = (CString)(BSTR) m_pIProjects->Item( CComVariant( iIndex ) )->Name;
if( iLastBackSlashIndex > -1 )
{
m_aoProjectsArray[iIndex - 1].m_oPathString = oFullNameString.Left( iLastBackSlashIndex );
}
else
{
m_aoProjectsArray[iIndex - 1].m_oPathString = oFullNameString;
}
m_aoProjectsArray[iIndex - 1].m_oPathString.MakeLower();
}
// sort projects by path length descending in order to assure correctly recognize project to which the file belongs
// in case when one path name includes path name from other project
qsort( m_aoProjectsArray.GetData(), m_aoProjectsArray.GetSize(), sizeof( CProject ), CompareProjectByPathLength );
}
int __cdecl CompareWindows( const void *elem1, const void *elem2 )
{
CWindowDescription *poWindow1 = (*(CWindowDescription **)elem1);
CWindowDescription *poWindow2 = (*(CWindowDescription **)elem2);
bool bWin1InActualProject = poWindow1->m_oProjectNameString.Compare( CWindowsListDialog::m_oActiveProjectNameString ) == 0;
bool bWin2InActualProject = poWindow2->m_oProjectNameString.Compare( CWindowsListDialog::m_oActiveProjectNameString ) == 0;
if( bWin1InActualProject )
{
if( bWin2InActualProject )
{
return poWindow1->m_oWindowContentString.Compare( poWindow2->m_oWindowContentString );
}
else
{
return -1;
}
}
else if( bWin2InActualProject )
{
return 1;
}
else
{
bool bWin1HasEmptyProject = ( poWindow1->m_oProjectNameString.IsEmpty() == TRUE );
bool bWin2HasEmptyProject = ( poWindow2->m_oProjectNameString.IsEmpty() == TRUE );
if( bWin1HasEmptyProject )
{
if( !bWin2HasEmptyProject )
{
return 1;
}
}
else
{
if( bWin2HasEmptyProject )
{
return -1;
}
}
int bProjectCompare = poWindow1->m_oProjectNameString.Compare( poWindow2->m_oProjectNameString );
if( bProjectCompare != 0 )
{
return bProjectCompare;
}
else
{
return poWindow1->m_oWindowContentString.Compare( poWindow2->m_oWindowContentString );
}
}
}
void CWindowsListDialog::GetWindowsDescriptions()
{
int iItemIndex = 0;
long lTmpWindowsListCount = m_pIWindows->Count;
m_aoWindowsDescriptionsArray.SetSize( lTmpWindowsListCount );
memset( m_aoWindowsDescriptionsArray.GetData(), 0, sizeof( CWindowDescription *) * lTmpWindowsListCount );
for( int iIndex = 1; iIndex <= lTmpWindowsListCount; iIndex++ )
{
::DSSharedObjects::IGenericWindowPtr pIWindow = m_pIWindows->Item( CComVariant( iIndex ) );
CString oCaptionString = (BSTR)pIWindow->Caption;
if( oCaptionString != "Disassembly" )// dissasebly window have no document
{
::DSSharedObjects::IGenericDocumentPtr pIDocument = pIWindow->Parent;
CWindowDescription *poWindowDescription = new CWindowDescription;
CString oWindowNameString = RemovePathAndDecorationsFromCaption( oCaptionString, pIDocument );
poWindowDescription->m_oFullPathString = (BSTR)pIDocument->FullName;
poWindowDescription->m_oProjectNameString = GetProjectNameForWindow( poWindowDescription->m_oFullPathString );
poWindowDescription->m_oWindowContentString = ReadWindowContent( poWindowDescription->m_oFullPathString, oWindowNameString, pIDocument );
poWindowDescription->m_iWindowIndex = iIndex;
m_aoWindowsDescriptionsArray[iItemIndex++] = poWindowDescription;
}
}
qsort( m_aoWindowsDescriptionsArray.GetData(), iItemIndex, sizeof( CWindowDescription* ), CompareWindows );
m_aoWindowsDescriptionsArray.SetSize( iItemIndex );
}
void CWindowsListDialog::DeleteWindowsDescriptions()
{
for( int iIndex = 0; iIndex < m_aoWindowsDescriptionsArray.GetSize(); iIndex++ )
{
if( m_aoWindowsDescriptionsArray[iIndex] )
{
delete m_aoWindowsDescriptionsArray[iIndex];
m_aoWindowsDescriptionsArray[iIndex] = NULL;
}
}
}
CString CWindowsListDialog::RemovePathAndDecorationsFromCaption( const CString &oWindowCaption, ::DSSharedObjects::IGenericDocumentPtr pIDocument )
{
CString oWindowNameString = oWindowCaption;
CString oWindowTypeString = (CString)(BSTR)pIDocument->Type;
int iBackslashIndex = oWindowNameString.ReverseFind( '\\' );
// remove file path
if( iBackslashIndex > -1 )
{
oWindowNameString.Delete( 0, iBackslashIndex + 1 );
}
// for opened resource windows remove resource file name
if( oWindowTypeString == "Generic" )
{
int iMinusIndex = oWindowNameString.Find( '-' );
if( iMinusIndex > 0
&& iMinusIndex + 1 < oWindowNameString.GetLength() )
{
oWindowNameString.Delete( 0, iMinusIndex + 2 );
for( int iIndex = 0; iIndex < oWindowNameString.GetLength(); iIndex++ )
{
if( oWindowNameString[iIndex] == ' ' )
{
oWindowNameString.Delete( iIndex, oWindowNameString.GetLength() - iIndex );
break;
}
}
}
}
return oWindowNameString;
}
CString CWindowsListDialog::GetProjectNameForWindow( const CString &oWindowFullPathString )
{
CString oLowerFileNameString = oWindowFullPathString;
oLowerFileNameString.MakeLower();
for( int iProjIndex = 0; iProjIndex < m_aoProjectsArray.GetSize(); iProjIndex++ )
{
if( m_aoProjectsArray[iProjIndex].m_oPathString == oLowerFileNameString.Left( m_aoProjectsArray[iProjIndex].m_oPathString.GetLength() ) )
{
return m_aoProjectsArray[iProjIndex].m_oNameString;
}
}
return "";
}
CString CWindowsListDialog::ReadWindowContent( const CString &oWindowFullPathString, const CString &oWindowCaptionString, ::DSSharedObjects::IGenericDocumentPtr pIDocument )
{
CString oWindowTypeString = (CString)(BSTR)pIDocument->Type;
CString oLineString;
EFileTypes eFileExtension = GetFileExtensionType( oWindowFullPathString );
CString oFileContentString = oWindowCaptionString; // get window caption as default window content
bool bContentWasFound = false;
if( oWindowTypeString != "Generic" )
{
switch( eFileExtension )
{
case eFileTypeCpp:
case eFileTypeHeader:
TRY
{
CStdioFile oFile( oWindowFullPathString, CFile::modeRead );
// analyse window content line by line
while( oFile.ReadString( oLineString ) && !bContentWasFound )
{
switch( eFileExtension )
{
case eFileTypeCpp:
if( FindFileContentInCppLine( oLineString, &oFileContentString ) )
{
bContentWasFound = true;
}
break;
case eFileTypeHeader:
if( FindFileContentInHeaderLine( oLineString, &oFileContentString ) )
{
bContentWasFound = true;
}
break;
}
}
}
CATCH( CFileException, poFileException )
{
}
END_CATCH
}
}
return oFileContentString;
}
CWindowsListDialog::EFileTypes CWindowsListDialog::GetFileExtensionType( const CString &oFileNameString )
{
EFileTypes eFileExtension = eFileTypeOther;
int iExtIndex = oFileNameString.ReverseFind( '.' );
if( iExtIndex > -1 )
{
CString oExtensionString = oFileNameString.Mid( iExtIndex );
if( oExtensionString.CompareNoCase( ".cpp" ) == 0 )
{
eFileExtension = eFileTypeCpp;
}
else if( oExtensionString.CompareNoCase( ".h" ) == 0 )
{
eFileExtension = eFileTypeHeader;
}
}
return eFileExtension;
}
bool CWindowsListDialog::FindFileContentInCppLine( const CString &oLineString, CString *poFileContentString )
{
int iDotIndex = oLineString.Find( "::" ); // look for class name in any method e.g. CString::CString()
if( iDotIndex > -1 )
{
int iRemIndex = oLineString.Find( "//" ); // omit commented class name
if( iRemIndex == -1 || iRemIndex > iDotIndex )
{
int iParanthesisIndex = oLineString.Find( '(', iDotIndex ); // find for paranthesis
if( iParanthesisIndex > -1 )
{
// class name is between '::' or white-space and '('
for( int iStartIndex = iDotIndex; iStartIndex > 0; iStartIndex-- )
{
if( isspace( oLineString[iStartIndex] ) )
{
iStartIndex++;
break;
}
}
CString oContentString = oLineString.Mid( iStartIndex, iDotIndex - iStartIndex );
if( IsItIdentifier( oContentString ) )
{
*poFileContentString = oContentString +" (.cpp)";
return true;
}
}
}
}
return false;
}
bool CWindowsListDialog::FindFileContentInHeaderLine( const CString &oLineString, CString *poFileContentString )
{
int iClassIndex = oLineString.Find( "class" );
if( iClassIndex > -1 )
{
int iRemIndex = oLineString.Find( "//" );
// ommit commented class name
if( iRemIndex == -1 || iClassIndex < iRemIndex )
{
int iSemicolonIndex = oLineString.Find( ';', iClassIndex );
// class name should not be finished by ';'
if( iSemicolonIndex == -1 )
{
*poFileContentString = "";
for( int iIndex = iClassIndex + sizeof( "class" ); iIndex < oLineString.GetLength(); iIndex++ )
{
char cCharacter = oLineString[iIndex];
if( !poFileContentString->IsEmpty() )
{
if( cCharacter == ':' ) // stop on ':' for inherited classes
{
break;
}
}
*poFileContentString += cCharacter;
}
// get only last word from content in order to ommit AFX_CLASS_EXPORT and other decorations
CutToLastWord( poFileContentString );
*poFileContentString += " (.h)";
return true;
}
}
}
return false;
}
void CWindowsListDialog::PopulateWindowsListCtrl()
{
CString oFilterString;
int iAddIndex = 0;
int iCurrentWindowData = -1, iCurrentWindowIndex = 0;
POSITION pFirstSelItemPosition = m_oWindowsListCtrl.GetFirstSelectedItemPosition();
if( pFirstSelItemPosition )
{
iCurrentWindowData = m_oWindowsListCtrl.GetItemData( m_oWindowsListCtrl.GetNextSelectedItem( pFirstSelItemPosition ) );
}
m_oWindowsListCtrl.SetRedraw( FALSE );
m_oWindowsListCtrl.DeleteAllItems();
m_oFilterEdit.GetWindowText( oFilterString );
for( int iIndex = 0; iIndex < m_aoWindowsDescriptionsArray.GetSize(); iIndex++ )
{
if( oFilterString.IsEmpty()
|| m_aoWindowsDescriptionsArray[iIndex]->IsContainFilteredWord( oFilterString ) )
{
m_oWindowsListCtrl.InsertItem( iAddIndex, "" );
m_oWindowsListCtrl.SetItemText( iAddIndex, eColumnIndexProject, m_aoWindowsDescriptionsArray[iIndex]->m_oProjectNameString );
m_oWindowsListCtrl.SetItemText( iAddIndex, eColumnIndexContent, m_aoWindowsDescriptionsArray[iIndex]->m_oWindowContentString );
m_oWindowsListCtrl.SetItemText( iAddIndex, eColumnIndexFullPath, m_aoWindowsDescriptionsArray[iIndex]->m_oFullPathString );
m_oWindowsListCtrl.SetItemData( iAddIndex, m_aoWindowsDescriptionsArray[iIndex]->m_iWindowIndex );
if( iCurrentWindowData == m_aoWindowsDescriptionsArray[iIndex]->m_iWindowIndex )
{
iCurrentWindowIndex = iAddIndex;
}
iAddIndex++;
}
}
if( m_oWindowsListCtrl.GetItemCount() > 0 )
{
m_oWindowsListCtrl.SetItemState( iCurrentWindowIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
}
m_oWindowsListCtrl.SetRedraw( TRUE );
}
void CWindowsListDialog::OnChangeEditFilter()
{
PopulateWindowsListCtrl();
}
void CWindowsListDialog::OnButtonAcvitate()
{
POSITION pSelPosition = m_oWindowsListCtrl.GetFirstSelectedItemPosition();
if( pSelPosition )
{
int iSelIndex = m_oWindowsListCtrl.GetNextSelectedItem( pSelPosition );
int iWindowIndex = m_oWindowsListCtrl.GetItemData( iSelIndex );
::DSSharedObjects::IGenericWindowPtr pIWindow = m_pIWindows->Item( CComVariant( iWindowIndex ) );
CDialog::OnOK();
pIWindow->Active = VARIANT_BOOL( -1 );
}
}
void CWindowsListDialog::OnButtonCloseWnd()
{
try
{
POSITION pSelPosition = m_oWindowsListCtrl.GetFirstSelectedItemPosition();
if( pSelPosition )
{
CArray <int,int> aiSelectedWindowIndex;
int iIndex = 0;
aiSelectedWindowIndex.SetSize( m_oWindowsListCtrl.GetSelectedCount() );
for( int iItemIndex = m_oWindowsListCtrl.GetItemCount() - 1; iItemIndex >= 0; iItemIndex-- )
{
if( m_oWindowsListCtrl.GetItemState( iItemIndex, LVIS_SELECTED ) == LVIS_SELECTED )
{
aiSelectedWindowIndex[iIndex++] = m_oWindowsListCtrl.GetItemData( iItemIndex );
}
}
int iMaxWndIndex = 0, iMaxIndex = -1;
do
{
iMaxWndIndex = 0, iMaxIndex = -1;
for( iIndex = aiSelectedWindowIndex.GetSize() - 1; iIndex >= 0 ; iIndex-- )
{
if( iMaxWndIndex < aiSelectedWindowIndex[iIndex] )
{
iMaxWndIndex = aiSelectedWindowIndex[iIndex];
iMaxIndex = iIndex;
}
}
if( iMaxIndex > -1 )
{
::DSSharedObjects::IGenericWindowPtr pIWindow = m_pIWindows->Item( CComVariant( iMaxWndIndex ) );
pIWindow->Close( CComVariant( dsSaveChangesPrompt ) );
aiSelectedWindowIndex[iMaxIndex] = 0;
}
}while( iMaxIndex > -1 );
}
GetWindowsDescriptions();
PopulateWindowsListCtrl();
}
catch( _com_error &oComError )
{
AfxMessageBox( oComError.Description() );
}
}
void CWindowsListDialog::OnDblclkListWindows(NMHDR* pNMHDR, LRESULT* pResult)
{
OnButtonAcvitate();
*pResult = 0;
}
void CWindowsListDialog::SaveDialogSettings()
{
CRegKey oRegKey;
if( oRegKey.Create( HKEY_LOCAL_MACHINE, oSettingsRegistryKey ) == ERROR_SUCCESS )
{
oRegKey.SetValue( m_oWindowsListCtrl.GetColumnWidth( eColumnIndexContent ), oSettingsValNameColWndContent );
oRegKey.SetValue( m_oWindowsListCtrl.GetColumnWidth( eColumnIndexProject ), oSettingsValNameColWndProject );
oRegKey.SetValue( m_oWindowsListCtrl.GetColumnWidth( eColumnIndexFullPath ), oSettingsValNameColWndFullPath );
oRegKey.Close();
}
}
void CWindowsListDialog::LoadDialogSettings()
{
CRegKey oRegKey;
CRect oRect;
if( oRegKey.Open( HKEY_LOCAL_MACHINE, oSettingsRegistryKey ) == ERROR_SUCCESS )
{
DWORD dwWidth = 0;
if( oRegKey.QueryValue( dwWidth, oSettingsValNameColWndContent ) == ERROR_SUCCESS )
{
m_oWindowsListCtrl.SetColumnWidth( eColumnIndexContent, dwWidth );
}
if( oRegKey.QueryValue( dwWidth, oSettingsValNameColWndProject ) == ERROR_SUCCESS )
{
m_oWindowsListCtrl.SetColumnWidth( eColumnIndexProject, dwWidth );
}
if( oRegKey.QueryValue( dwWidth, oSettingsValNameColWndFullPath ) == ERROR_SUCCESS )
{
m_oWindowsListCtrl.SetColumnWidth( eColumnIndexFullPath, dwWidth );
}
}
}
void CWindowsListDialog::OnDestroy()
{
CDialog::OnDestroy();
SaveDialogSettings();
}
BOOL CWindowsListDialog::PreTranslateMessage(MSG* pMsg)
{
if( pMsg->message == WM_KEYDOWN
&& pMsg->hwnd == m_oFilterEdit.GetSafeHwnd() )
{
switch( pMsg->wParam )
{
case VK_DOWN:
case VK_UP:
case VK_PRIOR:
case VK_NEXT:
m_oWindowsListCtrl.PostMessage( WM_KEYDOWN, pMsg->wParam, pMsg->lParam );
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
void CWindowsListDialog::CutToLastWord( CString *poString )
{
ASSERT( poString );
poString->TrimRight();
for( int iIndex = poString->GetLength() - 1; iIndex >= 0; iIndex-- )
{
if( ::isspace( poString->GetAt( iIndex ) ) )
{
poString->Delete( 0, iIndex + 1 );
return;
}
}
}
void CWindowsListDialog::OnContextMenu(CWnd* pWnd, CPoint point)
{
CMenu oMenu;
if( oMenu.LoadMenu( IDR_MENU_POPUP ) )
{
CMenu* poPopupMenu = oMenu.GetSubMenu( 0 );
ASSERT( poPopupMenu != NULL );
poPopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y,
AfxGetMainWnd() );
}
}
BOOL CWindowsListDialog::OnCommand(WPARAM wParam, LPARAM lParam)
{
switch( LOWORD( wParam ) )
{
case IDM_KEYBINDINGS:
OnKeyBindingsDialog();
return TRUE;
case IDM_ABOUT:
OnAbout();
return TRUE;
}
return CDialog::OnCommand(wParam, lParam);
}
void CWindowsListDialog::OnKeyBindingsDialog()
{
CKeyBindingsDialog oKeyBindingsDialog( m_pIApplication, this );
oKeyBindingsDialog.DoModal();
}
void CWindowsListDialog::OnAbout()
{
CAboutDialog oAboutDialog;
oAboutDialog.DoModal();
}
bool CWindowsListDialog::IsItIdentifier( const CString &oString )
{
if( !oString.IsEmpty()
&&
( oString[0] >= 'A' && oString[0] <= 'Z'
|| oString[0] >= 'a' && oString[0] <= 'z'
|| oString[0] == '_'
)
)
{
for( int iIndex = 1; iIndex < oString.GetLength(); iIndex++ )
{
if( oString[0] >= 'A' && oString[0] <= 'Z'
|| oString[0] >= 'a' && oString[0] <= 'z'
|| oString[0] >= '0' && oString[0] <= '9'
|| oString[iIndex] == '_' )
{
continue;
}
else
{
return false;
}
}
return true;
}
return false;
}