Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Counting Lines in a MS VC++ 6.0 project

, 4 Aug 2001
A program that counts the lines (source, comments, blank) in every file included in a MS VC++ 6.0 project
// THIS FILE IS PROVIDED FOR INFORMATIONAL PURPOSE ONLY.
// NO COMERCIAL USE!!!
// Information: decl_spec@yahoo.com

#include "stdafx.h"
#include "LineCounter.h"
#include "LineCounterDlg.h"
#include "linecounter_.h"
#include "lcprojectparser.h"
#include "ViewCodeDlg.h"

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

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CLineCounterDlg::CLineCounterDlg(CWnd* pParent /*=NULL*/) : m_compare_struct(NULL), m_imagelist(NULL),
	CDialog(CLineCounterDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CLineCounterDlg)
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

CLineCounterDlg::~CLineCounterDlg()
{
	delete [] m_compare_struct;
	delete m_imagelist;
}

void CLineCounterDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CLineCounterDlg)
	DDX_Control(pDX, ID_LINES_TOTAL, m_LinesTotal);
	DDX_Control(pDX, ID_LINES_SOURCE, m_LinesSource);
	DDX_Control(pDX, ID_LINES_COMMENT, m_LinesComment);
	DDX_Control(pDX, ID_LINES_BLANK, m_LinesBlank);
	DDX_Control(pDX, ID_PROGRESS_LABEL, m_ProgressLabel);
	DDX_Control(pDX, ID_PROGRESSBAR, m_Progress);
	DDX_Control(pDX, ID_PROJECTNAME, m_ProjectName);
	DDX_Control(pDX, ID_FILELIST, m_FileList);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CLineCounterDlg, CDialog)
	//{{AFX_MSG_MAP(CLineCounterDlg)
	ON_COMMAND(ID_FILE_EXIT, OnFileExit)
	ON_NOTIFY(LVN_COLUMNCLICK, ID_FILELIST, OnColumnclickFilelist)
	ON_COMMAND(ID_FILE_SELECTPROJECT, OnFileSelectproject)
	ON_COMMAND(ID_FILE_COUNT, OnFileCount)
	ON_WM_CLOSE()
	ON_NOTIFY(NM_RCLICK, ID_FILELIST, OnRclickFilelist)
	ON_COMMAND(ID_SOURCE_VIEW, OnSourceView)
	ON_COMMAND(ID_HELP_ABOUT, OnHelpAbout)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLineCounterDlg message handlers

BOOL CLineCounterDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	UpdateData(TRUE);

	HMENU sys_menu = ::GetSystemMenu(GetSafeHwnd(), FALSE);

	RemoveMenu(sys_menu, 2, MF_BYPOSITION);
	RemoveMenu(sys_menu, 3, MF_BYPOSITION);
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	ListView_SetExtendedListViewStyle(m_FileList.GetSafeHwnd(), LVS_EX_FULLROWSELECT);

	m_FileList.InsertColumn(0, "File Name", LVCFMT_LEFT, 150);
	m_FileList.InsertColumn(1, "Total Lines", LVCFMT_LEFT, 85);
	m_FileList.InsertColumn(2, "Source Lines", LVCFMT_LEFT, 100);
	m_FileList.InsertColumn(3, "Comment Lines", LVCFMT_LEFT, 100);
	m_FileList.InsertColumn(4, "Blank Lines", LVCFMT_LEFT, 100);


	m_imagelist = new CImageList;
	m_imagelist->Create(16, 16, ILC_MASK, 2, 2);
	m_imagelist->Add(AfxGetApp()->LoadIcon(MAKEINTRESOURCE(IDI_PERCENT)));

	HD_ITEM curItem;
	CHeaderCtrl* pHdrCtrl= NULL;
	pHdrCtrl= m_FileList.GetHeaderCtrl();
	CImageList *result = pHdrCtrl->SetImageList(m_imagelist);
	curItem.mask= HDI_IMAGE | HDI_FORMAT;
	curItem.iImage= 0;
	curItem.fmt= HDF_LEFT | HDF_IMAGE | HDF_STRING;
	
	for (int h = 0; h < 5; h++)
	{
		pHdrCtrl->GetItem(h, &curItem);
		pHdrCtrl->SetItem(h, &curItem);
	}

	char *message = "[No Project Selected]";
	m_ProjectName.SetWindowText(message);
	m_Progress.SetPos(0);
	m_ProgressLabel.SetWindowText(message);
	m_LinesTotal.SetWindowText(message);
	m_LinesSource.SetWindowText(message);
	m_LinesComment.SetWindowText(message);
	m_LinesBlank.SetWindowText(message);

	m_compare_order[0] = 1;
	m_compare_order[1] = m_compare_order[2] = m_compare_order[3] = m_compare_order[4] = m_compare_order[0];

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CLineCounterDlg::OnFileExit() 
{
	// We call OnOK to safely close the application
	CDialog::OnOK();
}

// This one is tricky...
// We have 4 columns showing number (percent), i.e. 100 (78%), meaning
// the file has 100 lines of that type, which is 78% from the total amount
// of lines in the file.
// To sort the columns using the number you must click on 3/4's of the column header
// To sort the column using the percent you must click on the last 1/4 of the column header
//
// "That's all folks" :)

void CLineCounterDlg::OnColumnclickFilelist(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

	m_sorting_column = pNMListView->iSubItem;

	SetCursor(LoadCursor(NULL, IDC_WAIT));
	// if user clicked one of the "percent column"...
	if (m_sorting_column > 1)
	{
		POINT p;
		DWORD pos;

		pos = GetMessagePos();
		p.x = LOWORD(pos);
		p.y = HIWORD(pos);

		::ScreenToClient(m_FileList.GetHeaderCtrl()->GetSafeHwnd(), &p);

		int width = 0;
		for (int i = 0; i < m_sorting_column; i++)
			width += ListView_GetColumnWidth(m_FileList.GetSafeHwnd(), i);

		int current_width = ListView_GetColumnWidth(m_FileList.GetSafeHwnd(), m_sorting_column);
		// ...checkin coords...
		if (p.x > (width + (current_width / 1.2)))
			m_sorting_column = -m_sorting_column;
	}
	
	::SendMessage(m_FileList.GetSafeHwnd(), LVM_SORTITEMS, (LPARAM)this, (LPARAM)(PFNLVCOMPARE)CLineCounterDlg::CompareFunc);

	SetCursor(LoadCursor(NULL, IDC_ARROW));

	if (m_sorting_column < 0)
		m_sorting_column = -m_sorting_column;
	m_compare_order[m_sorting_column] = !m_compare_order[m_sorting_column];
	
	*pResult = 0;
}

void CLineCounterDlg::OnFileSelectproject() 
{
	static char extension[] = "Microsoft Visual C++ 6.0 Projects (*.dsp)|*.dsp|All Files (*.*)|*.*||";
	CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, extension);

	int result = dlg.DoModal();
	if (result != 1)
		return;

	static char message[] = "Select Count! to start...";
	delete [] m_compare_struct;
	m_compare_struct = NULL;
	m_project = dlg.GetPathName();
	m_ProjectName.SetWindowText(m_project);
	m_ProgressLabel.SetWindowText(message);
	ListView_DeleteAllItems(m_FileList.GetSafeHwnd());

	m_LinesTotal.SetWindowText(message);
	m_LinesSource.SetWindowText(message);
	m_LinesComment.SetWindowText(message);
	m_LinesBlank.SetWindowText(message);
}

void CLineCounterDlg::OnFileCount() 
{
	if (m_project.IsEmpty())
	{
		MessageBox("No project selected!", "Error", MB_ICONERROR);
		return;
	}

	srcmng sources;
	int nf = ProcessDSP(m_project, sources);
	lsrc_item *item;
	item = sources.srcs;

	ListView_DeleteAllItems(m_FileList.GetSafeHwnd());

	if (!item)
	{
		MessageBox("Empty project or not a project file!", "Info", MB_ICONEXCLAMATION);
		m_FileList.UnlockWindowUpdate();
		return;
	}

	int size = m_project.GetLength();
	const char *project = (const char*)m_project;

	while (size > -1 && project[size--] != '\\')
		;

	if (size == -1)
		return;

	size += 2;
	char *dir = new char[size + 1];
	memcpy(dir, project, size);
	dir[size] = 0;
	int dir_size = strlen(dir);
	
	m_LinesTotal.SetWindowText("Working...");
	m_LinesSource.SetWindowText("Working...");
	m_LinesComment.SetWindowText("Working...");
	m_LinesBlank.SetWindowText("Working...");

	UpdateWindow();
	SetCursor(LoadCursor(NULL, IDC_WAIT));
	m_FileList.LockWindowUpdate();

	int count = sources.getcount();
	char progress[18];
	m_Progress.SetPos(0);
	m_Progress.SetRange(0, count);
	m_ProgressLabel.ShowWindow(SW_SHOW);
	m_Progress.ShowWindow(SW_SHOW);

	char *filename = NULL;
	LVITEM list_item;
	int id = 0;
	char format[128];
	int lines_total		= 0;
	int lines_source	= 0;
	int lines_comment	= 0;
	int lines_blank		= 0;
	float percent		= 0;

	delete [] m_compare_struct;
	m_compare_struct = new compare_struct[count];

	while (item)
	{
		filename = new char[dir_size + strlen(item->filename) + 1];
		sprintf(filename, "%s%s", dir, item->filename);
		ProcessFile(filename, item);
		delete [] filename;

		m_compare_struct[id].m_filename = new char[strlen(item->filename) + 1];
		strcpy(m_compare_struct[id].m_filename, item->filename);
		m_compare_struct[id].m_total = item->lc;
		m_compare_struct[id].m_source = item->elc;
		m_compare_struct[id].m_comment = item->cc;
		m_compare_struct[id].m_blank = item->blc;

		list_item.mask = LVIF_TEXT | LVIF_PARAM;
		list_item.iItem = id++;
		list_item.iSubItem = 0;
		list_item.lParam = (LPARAM)id-1;
		list_item.pszText = item->filename;
		int index = ListView_InsertItem(m_FileList.GetSafeHwnd(), &list_item);

		// LinesTotal 
		lines_total += item->lc;
		list_item.mask = LVIF_TEXT;
		list_item.iItem = id-1;
		list_item.iSubItem = 1;
		sprintf(format, "%d", item->lc);
		list_item.lParam = item->lc;
		list_item.pszText = format;
		ListView_SetItem(m_FileList.GetSafeHwnd(), &list_item);

		// LinesSource
		lines_source += item->elc;
		list_item.mask = LVIF_TEXT;
		list_item.iItem = id-1;
		list_item.iSubItem = 2;
		percent = (item->elc == 0 || item->lc == 0) ? 0 : (float)(100 * item->elc) / item->lc;
		m_compare_struct[id-1].m_source_percent = percent;
		sprintf(format, "%d (%.2f%%)", item->elc, percent);
		list_item.pszText = format;
		ListView_SetItem(m_FileList.GetSafeHwnd(), &list_item);

		// LinesComment
		lines_comment += item->cc;
		list_item.mask = LVIF_TEXT;
		list_item.iItem = id-1;
		percent = (item->cc == 0 || item->lc == 0) ? 0 : (float)(100 * item->cc) / item->lc;
		m_compare_struct[id-1].m_comment_percent = percent;
		sprintf(format, "%d (%.2f%%)", item->cc, percent);
		list_item.pszText = format;
		list_item.iSubItem = 3;
		ListView_SetItem(m_FileList.GetSafeHwnd(), &list_item);

		// LinesBlank
		lines_blank += item->blc;
		list_item.mask = LVIF_TEXT;
		list_item.iItem = id-1;
		percent = (item->blc == 0 || item->lc == 0) ? 0 : (float)(100 * item->blc) / item->lc;
		m_compare_struct[id-1].m_blank_percent = percent;
		sprintf(format, "%d (%.2f%%)", item->blc, percent);
		list_item.pszText = format;
		list_item.iSubItem = 4;
		ListView_SetItem(m_FileList.GetSafeHwnd(), &list_item);

		sprintf(progress, "%d%% processed...", (100 * (id+1))/ count);
		m_ProgressLabel.SetWindowText(progress);
		m_Progress.SetPos(id);

		item = item->next; // Next, please!
	}

	sprintf(format, "%d", lines_total);
	m_LinesTotal.SetWindowText(format);
	sprintf(format, "%d (%.9f%% of total)", lines_source, 
		(lines_source == 0 || lines_total == 0) ? 0 : (float)(100 * lines_source) / lines_total);
	m_LinesSource.SetWindowText(format);
	sprintf(format, "%d (%.9f%% of total)", lines_comment, 
		(lines_comment == 0 || lines_total == 0) ? 0 : (float)(100 * lines_comment) / lines_total);
	m_LinesComment.SetWindowText(format);
	sprintf(format, "%d (%.9f%% of total)", lines_blank, 
		(lines_blank == 0 || lines_total == 0) ? 0 : (float)(100 * lines_blank) / lines_total);
	m_LinesBlank.SetWindowText(format);

	m_ProgressLabel.SetWindowText("All done");
	m_Progress.ShowWindow(SW_HIDE);
	delete [] dir;

	m_sorting_column = 0;
	::SendMessage(m_FileList.GetSafeHwnd(), LVM_SORTITEMS, (LPARAM)this, (LPARAM)(PFNLVCOMPARE)CLineCounterDlg::CompareFunc);

	SetCursor(LoadCursor(NULL, IDC_ARROW));
	m_FileList.UnlockWindowUpdate();
}

// OnOK and OnCancel are declared here to prevent the CDialog functions from being called,
// thus preventing closing the dialog when clicking ENTER or ESC
void CLineCounterDlg::OnOK()
{
}

void CLineCounterDlg::OnCancel()
{
}

void CLineCounterDlg::OnClose() 
{	
	CDialog::OnOK();
}

void CLineCounterDlg::OnRclickFilelist(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pList = (NM_LISTVIEW*)pNMHDR;

	HMENU menu = LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(ID_POPUP));
	menu = GetSubMenu(menu, 0);

	if (m_project.IsEmpty())
		ModifyMenu(menu, 0, MF_BYPOSITION | MF_GRAYED, NULL, "[No Project Selected]");
	else
		if (pList->iItem < 0)
			ModifyMenu(menu, 0, MF_BYPOSITION | MF_GRAYED, NULL, "No File Selected");

	POINT p;
	GetCursorPos(&p);
	TrackPopupMenu(menu, TPM_LEFTALIGN, p.x, p.y, NULL, GetSafeHwnd(), NULL);
	*pResult = 0;
}

void CLineCounterDlg::OnSourceView() 
{
	CViewCodeDlg dlg;

	char file_name[1024];
	POSITION pos = m_FileList.GetFirstSelectedItemPosition();
	if (!pos)
		return;

	int id = m_FileList.GetNextSelectedItem(pos);
	m_FileList.GetItemText(id, 0, file_name, 1024);

	int size = m_project.GetLength();
	const char *project = (const char*)m_project;

	while (size > -1 && project[size--] != '\\')
		;

	if (size == -1)
		return;

	size += 2;
	char *filename = new char[size + strlen(file_name) + 1];
	memcpy(filename, project, size);
	strcpy(filename + size, file_name);

	dlg.SetFileName(filename);
	delete [] filename;

	dlg.DoModal();
}

int CALLBACK CLineCounterDlg::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CLineCounterDlg *dlg = (CLineCounterDlg*)lParamSort;

	if (dlg->m_sorting_column == 0) // sorting by File Name
	{
		if (!dlg->m_compare_struct)
			return 0;
		const char *data1 = dlg->m_compare_struct[lParam1].m_filename;
		const char *data2 = dlg->m_compare_struct[lParam2].m_filename;
		if (dlg->m_compare_order[0])
			return (strcmpi(data1, data2));
		else
			return (strcmpi(data2, data1));
	}
	else // Sorting by Total Lines/Source Lines/Comment Lines/Blank Lines
	{
		float data1 = -1;
		float data2 = -1;

		if (dlg->m_sorting_column > 0)
		{
			if (!dlg->m_compare_struct)
				return 0;

			if (dlg->m_sorting_column == 1)
			{
				data1 = (float)dlg->m_compare_struct[lParam1].m_total;
				data2 = (float)dlg->m_compare_struct[lParam2].m_total;
			}
			else
			{
				if (dlg->m_sorting_column == 2)
				{
					data1 = (float)dlg->m_compare_struct[lParam1].m_source;
					data2 = (float)dlg->m_compare_struct[lParam2].m_source;
				}
				else
				{
					if (dlg->m_sorting_column == 3)
					{
						data1 = (float)dlg->m_compare_struct[lParam1].m_comment;
						data2 = (float)dlg->m_compare_struct[lParam2].m_comment;
					}
					else
					{
						if (dlg->m_sorting_column == 4)
						{
							data1 = (float)dlg->m_compare_struct[lParam1].m_blank;
							data2 = (float)dlg->m_compare_struct[lParam2].m_blank;
						}
					}
				}
			}
		}
		else // percent sorting
		{
			if (dlg->m_sorting_column == -2)
			{
				data1 = dlg->m_compare_struct[lParam1].m_source_percent;
				data2 = dlg->m_compare_struct[lParam2].m_source_percent;
			}
			else
			{
				if (dlg->m_sorting_column == -3)
				{
					data1 = dlg->m_compare_struct[lParam1].m_comment_percent;
					data2 = dlg->m_compare_struct[lParam2].m_comment_percent;
				}
				else
				{
					if (dlg->m_sorting_column == -4)
					{
						data1 = dlg->m_compare_struct[lParam1].m_blank_percent;
						data2 = dlg->m_compare_struct[lParam2].m_blank_percent;
					}
				}
			}
		}

		if (dlg->m_compare_order[dlg->m_sorting_column > 0 ? dlg->m_sorting_column : -dlg->m_sorting_column])
			return (int)data1 - (int)data2;
		else
			return (int)data2 - (int)data1;
	}

	return 0;
}

void CLineCounterDlg::OnHelpAbout() 
{
	CDialog about(IDD_ABOUTBOX);
	about.DoModal();
}

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

Share

About the Author

_declspec

United States United States
No Biography provided

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 5 Aug 2001
Article Copyright 2001 by _declspec
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid