Index: astyleplugin.cpp
===================================================================
--- astyleplugin.cpp	(revision 4944)
+++ astyleplugin.cpp	(working copy)
@@ -24,12 +24,25 @@
 #include <wx/xrc/xmlres.h>
 #include <wx/fs_zip.h>
 #include <wx/strconv.h>
+#include <wx/progdlg.h>
 #include "asstreamiterator.h"
 #include "cbstyledtextctrl.h"
 
 using std::istringstream;
 using std::string;
 
+namespace
+{
+	const int idCodeFormatterFile = wxNewId();
+	const int idCodeFormatterActiveFile = wxNewId();
+	const int idCodeFormatterProject = wxNewId();
+}
+
+BEGIN_EVENT_TABLE( AStylePlugin, cbPlugin )
+	EVT_MENU( idCodeFormatterActiveFile, AStylePlugin::OnFormatActiveFile )
+	EVT_MENU( idCodeFormatterProject, AStylePlugin::OnFormatProject )
+END_EVENT_TABLE()
+
 // this auto-registers the plugin
 
 namespace
@@ -78,6 +91,97 @@
     return dlg;
 }
 
+void AStylePlugin::BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data )
+{
+	if ( !menu || !IsAttached() )
+		return;
+
+	switch ( type )
+	{
+		case mtEditorManager:
+			menu->AppendSeparator();
+			menu->Append( idCodeFormatterActiveFile, _( "Format This File (AStyle)" ), _( "Format the source code in the current file" ) );
+			break;
+
+		case mtProjectManager:
+			if ( data ) switch ( data->GetKind() )
+				{
+					case FileTreeData::ftdkProject:
+						menu->AppendSeparator();
+						menu->Append( idCodeFormatterProject, _( "Format This Project (AStyle)" ), _( "Format the source code in this project" ) );
+						break;
+
+					case FileTreeData::ftdkFile:
+						menu->AppendSeparator();
+						menu->Append( idCodeFormatterProject, _( "Format This File (AStyle)" ), _( "Format the source code in this file" ) );
+						break;
+
+					default:
+						// Do nothing.
+						break;
+				}
+			break;
+
+		default:
+			break;
+	}
+}
+
+void AStylePlugin::OnFormatProject( wxCommandEvent& event )
+{
+	wxTreeCtrl *tree = Manager::Get()->GetProjectManager()->GetTree();
+
+	if ( NULL == tree )
+		return;
+
+	wxTreeItemId treeItem =  tree->GetSelection();
+
+	if ( false == treeItem.IsOk() )
+		return;
+
+	const FileTreeData *data = static_cast<FileTreeData*>( tree->GetItemData( treeItem ) );
+
+	if ( NULL == data )
+		return;
+
+	switch ( data->GetKind() )
+	{
+		case FileTreeData::ftdkProject:
+			{
+				cbProject* prj = data->GetProject();
+				wxProgressDialog progressDlg(_("Please wait"), _("Formatting..."), prj->GetFilesCount(), NULL, wxPD_CAN_ABORT|wxPD_AUTO_HIDE|wxPD_SMOOTH );
+				progressDlg.Show();
+				for ( int i = 0; i < prj->GetFilesCount(); ++i )
+				{
+					ProjectFile* pf = prj->GetFile( i );
+					wxString filename = pf->file.GetFullPath();
+
+					FileType fileType = FileTypeOf( filename );
+					if ( fileType == ftSource || fileType == ftHeader )
+					{
+						FormatFile( filename );
+						if ( false == progressDlg.Update( i, wxString("Formatting ") + pf->relativeFilename ) )
+							break;
+					}
+				}
+			}
+			break;
+
+		case FileTreeData::ftdkFile:
+			{
+				ProjectFile* f = data->GetProject()->GetFile( data->GetFileIndex() );
+				if ( f )
+					FormatFile( f->file.GetFullPath() );
+			}
+			break;
+	}
+}
+
+void AStylePlugin::OnFormatActiveFile( wxCommandEvent& event )
+{
+		Execute();
+}
+
 int AStylePlugin::Execute()
 {
     if (!IsAttached())
@@ -92,10 +196,96 @@
         return 0;
     }
 
+    FormatEditor( ed );
+}
+
+void AStylePlugin::FormatFile( const wxString &filename )
+{
+	cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filename );
+
+	if ( ed )
+	{
+		// File is already open
+		FormatEditor( ed );
+	}
+	else
+	{
+		// File is not open.  We must open it.
+		ed = Manager::Get()->GetEditorManager()->Open( filename );
+
+		if ( ed )
+		{
+			bool changed = FormatEditor( ed );
+
+			if ( !changed )
+			{
+				// We opened a file and it didn't change.  Close it.
+				Manager::Get()->GetEditorManager()->Close( filename );
+			}
+		}
+	}
+}
+
+// Special code to compare strings which doesn't care
+// about spaces leading up to the EOL.
+static bool BuffersDiffer( const wxString &a, const wxString &b )
+{
+	const wxChar *aCurrent = a.c_str();
+	const wxChar *bCurrent = b.c_str();
+	const wxChar * const aEnd = aCurrent + a.Len();
+	const wxChar * const bEnd = bCurrent + b.Len();
+
+	while ( aCurrent != aEnd && bCurrent != bEnd )
+	{
+		if ( *aCurrent != *bCurrent )
+		{
+			// Check for varying space at EOL
+			while ( *aCurrent == ' ' || *aCurrent == '\t' )
+			{
+				if ( ++aCurrent == aEnd )
+					break;
+			}
+			while ( *bCurrent == ' ' || *bCurrent == '\t' )
+			{
+				if ( ++bCurrent == bEnd )
+					break;
+			}
+
+			// Make sure it was at EOL
+			if ( ( *aCurrent != '\r' && *aCurrent != '\n' ) || ( *bCurrent != '\r' && *bCurrent != '\n' ) )
+				return true;
+		}
+
+		++aCurrent;
+		++bCurrent;
+	}
+
+	while ( aCurrent != aEnd )
+	{
+		if ( *aCurrent != ' ' && *aCurrent != '\t' )
+		{
+			return true;
+		}
+
+	}
+
+	while ( bCurrent != bEnd )
+	{
+		if ( *bCurrent != ' ' && *bCurrent != '\t' )
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool AStylePlugin::FormatEditor( cbEditor *ed )
+{
     if (ed->GetControl()->GetReadOnly())
     {
         cbMessageBox(_("The file is read-only"), _("Error"), wxICON_ERROR);
-        return 0;
+        return false;
     }
 
     wxString edText(ed->GetControl()->GetText());
@@ -167,20 +357,24 @@
 
     int pos = ed->GetControl()->GetCurrentPos();
 
-    ed->GetControl()->BeginUndoAction();
-    ed->GetControl()->SetText(formattedText);
+    bool changed = BuffersDiffer( formattedText, edText );
 
-    for (std::vector<int>::const_iterator i = new_bookmark.begin(); i != new_bookmark.end(); ++i)
-    {
-        ed->ToggleBookmark(*i);
-    }
+	if ( changed )
+	{
+		ed->GetControl()->BeginUndoAction();
+		ed->GetControl()->SetText(formattedText);
 
-    ed->GetControl()->EndUndoAction();
+		for (std::vector<int>::const_iterator i = new_bookmark.begin(); i != new_bookmark.end(); ++i)
+		{
+			ed->ToggleBookmark(*i);
+		}
 
-    ed->GetControl()->GotoPos(pos);
-    ed->SetModified(true);
+		ed->GetControl()->EndUndoAction();
+		ed->GetControl()->GotoPos(pos);
+		ed->SetModified(true);
+	}
 
     wxSetCursor(wxNullCursor);
 
-    return 0;
+	return changed;
 }
Index: astyleplugin.h
===================================================================
--- astyleplugin.h	(revision 4944)
+++ astyleplugin.h	(working copy)
@@ -28,9 +28,18 @@
     int Configure();
     int GetConfigurationGroup() const { return cgEditor; }
     cbConfigurationPanel* GetConfigurationPanel(wxWindow* parent);
+	void BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data = 0 );
     int Execute();
+
+	void OnFormatActiveFile( wxCommandEvent& event );
+	void OnFormatProject( wxCommandEvent& event );
+	void FormatFile( const wxString &filename );
+	bool FormatEditor( cbEditor *ed );
+
     void OnAttach(); // fires when the plugin is attached to the application
     void OnRelease(bool appShutDown); // fires when the plugin is released from the application
+
+	DECLARE_EVENT_TABLE()
 };
 
 #endif // ASTYLEPLUGIN_H
