using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Reflector;
using Reflector.CodeModel;
namespace AutoDiagramer
{
#region AutoDiagrammerWindow CLASS
/// <summary>
/// This class provides one part to be able to be a Lutz Roeders Reflector
/// AddIn. The other fil is called AutoDiagrammerPackage. This part provides
/// the actual control that is shown when the user clicks on the menu
/// within Reflector
/// </summary>
public partial class AutoDiagrammerWindow : UserControl
{
#region Instance Fields
private IAssemblyBrowser _assemblyBrowser;
private ClassDrawerContainerPanel _pnlFlowClasses;
private BitmapPrintPageEventHandler _BitmapPrintPageEventHandler;
private IAssembly _assembly;
#endregion
#region Ctor
/// <summary>
/// Constructs a new AutoDiagrammerWindow, using Lutz Roeders
/// IServiceProvider interface, for reflector Add-Ins
/// </summary>
/// <param name="serviceProvider"></param>
public AutoDiagrammerWindow(IServiceProvider serviceProvider)
{
this._assemblyBrowser = (IAssemblyBrowser)serviceProvider.GetService(typeof(IAssemblyBrowser));
//wire up _assemblyBrowser ActiveItemChanged event
if (this._assemblyBrowser==null)
this._assemblyBrowser.ActiveItemChanged -= new EventHandler(activeItemChanged);
if (this._assemblyBrowser!=null)
this._assemblyBrowser.ActiveItemChanged +=new EventHandler(activeItemChanged);
InitializeComponent();
//create a new ClassDrawerContainerPanel to hold individual classes
createPanel();
pnlHolderMain.Controls.Add(_pnlFlowClasses);
btnZoom.Enabled=false;
btnSaveDiagram.Enabled=false;
btnPrint.Enabled=false;
lblWait.Visible=false;
pgWait.Visible=false;
}
#endregion
#region Private methods
/// <summary>
/// creates the ClassDrawerContainerPanel
/// </summary>
private void createPanel()
{
this._pnlFlowClasses = new ClassDrawerContainerPanel();
this._pnlFlowClasses.AutoScroll = true;
this._pnlFlowClasses.BackColor = Color.White;
this._pnlFlowClasses.Dock = DockStyle.Fill;
this._pnlFlowClasses.Location = new System.Drawing.Point(0,0);
this._pnlFlowClasses.Name = "pnlFlowClasses";
this._pnlFlowClasses.Size = new System.Drawing.Size(569, 391);
this._pnlFlowClasses.TabIndex = 0;
this.pnlHolderMain.Controls.Add(_pnlFlowClasses);
}
#region CheckAssembly
/// <summary>
/// Lutz Roeders assemblyBrowser.ActiveItemChanged event
/// Check that the actve Item is an assembly, and that its
/// not "mscorlib" or "system" I Dont allow them
/// </summary>
/// <param name="sender">assemblyBrowser</param>
/// <param name="e">the event args</param>
private void activeItemChanged(object sender, EventArgs e)
{
if (isDrawableType())
{
pnlTop.Invalidate();
DrawDiagram();
}
}
/// <summary>
/// Returns true if the assemblyBrowser.ActiveItem can be drawn by the
/// AutoDiagrammer addin
/// </summary>
/// <returns>true if the assemblyBrowser.ActiveItem can be drawn by the
/// AutoDiagrammer addin</returns>
private bool isDrawableType()
{
bool retDefault=false;
// testing if current item is an assembly
_assembly = this._assemblyBrowser.ActiveItem as IAssembly;
if (_assembly!=null)
{
if (!_assembly.Name.ToLower().Equals("mscorlib") &&
!_assembly.Name.ToLower().Equals("system") &&
!_assembly.Name.ToLower().Equals("autodiagrammer"))
{
return true;
}
}
return retDefault;
}
#endregion
#region Add methods for DrawableClass
/// <summary>
/// Does a call to whichever Add method matches
/// the results of calling the value.Modules on the current
/// IAssembly value parameter
/// </summary>
/// <param name="list">generic list of <see cref="DrawableClass">DrawableClass</see></param>
/// <param name="value">A Reflector IAssembly type</param>
private void Add(IList<DrawableClass> list, IAssembly value)
{
foreach (IModule module in value.Modules)
{
this.Add(list, module);
}
}
/// <summary>
/// Does a call to whichever Add method matches
/// the results of calling the value.Types on the current
/// IModule value parameter
/// </summary>
/// <param name="list">generic list of <see cref="DrawableClass">DrawableClass</see></param>
/// <param name="value">A Reflector IModule type</param>
private void Add(IList<DrawableClass> list, IModule value)
{
foreach (ITypeDeclaration typeDeclaration in value.Types)
{
this.Add(list, typeDeclaration);
}
}
/// <summary>
/// Does a call to whichever Add method matches
/// the results of calling the value.Types on the current
/// INamespace value parameter
/// </summary>
/// <param name="list">generic list of <see cref="DrawableClass">DrawableClass</see></param>
/// <param name="value">A Reflector INamespace type</param>
private void Add(IList<DrawableClass> list, INamespace value)
{
foreach (ITypeDeclaration typeDeclaration in value.Types)
{
this.Add(list, typeDeclaration);
}
}
/// <summary>
/// Adds a new <see cref="DrawableClass">DrawableClass</see> object to the internal
/// <see cref="ClassDrawerContainerPanel">ClassDrawerContainerPanel</see>
/// </summary>
/// <param name="list">generic list of DrawableClass</param>
/// <param name="value">A Reflector ITypeDeclaration type</param>
private void Add(IList<DrawableClass> list, ITypeDeclaration value)
{
DrawableClass dc = new DrawableClass(value);
dc.ClassStartColor = Program._ClassStartColor;
dc.ClassEndColor = Program._ClassEndColor;
dc.ClassBorderColor = Program._ClassBorderColor;
dc.RegenerateControl();
dc.BackColor = _pnlFlowClasses.BackColor;
list.Add(dc);
}
#endregion
#region Button click methods
/// <summary>
/// DRaws the diagram for the given Assembly
/// </summary>
/// <param name="sender">btnAbout</param>
/// <param name="e">the event args</param>
private void btnDrawDiagram_Click(object sender, EventArgs e)
{
if (isDrawableType())
{
DrawDiagram();
}
}
/// <summary>
/// Draws the diagram for the current Assembly
/// </summary>
private void DrawDiagram()
{
//this._pnlFlowClasses.Controls.Clear();
this._pnlFlowClasses.Visible = false;
this.pnlHolderMain.Controls.Remove(_pnlFlowClasses);
createPanel();
this._pnlFlowClasses.Visible = false;
btnZoom.Enabled=false;
btnSaveDiagram.Enabled=false;
btnPrint.Enabled=false;
lblWait.Visible=true;
pgWait.Visible=true;
Application.DoEvents();
//get the list of drawable classes, and create a new panel to contain
//teh diagram
List<DrawableClass> dccs = new List<DrawableClass>();
ITypeDeclaration typeDeclaration = this._assemblyBrowser.ActiveItem as ITypeDeclaration;
if (typeDeclaration != null)
{
this.Add(dccs, typeDeclaration);
}
INamespace namespaceName = this._assemblyBrowser.ActiveItem as INamespace;
if (namespaceName != null)
{
this.Add(dccs, namespaceName);
}
IModule module = this._assemblyBrowser.ActiveItem as IModule;
if (module != null)
{
this.Add(dccs, module);
}
IAssembly assembly = this._assemblyBrowser.ActiveItem as IAssembly;
if (assembly != null)
{
this.Add(dccs, assembly);
}
this._pnlFlowClasses.ClassesToDraw = dccs;
this._pnlFlowClasses.LayoutControls();
btnZoom.Enabled=true;
btnSaveDiagram.Enabled=true;
btnPrint.Enabled=true;
lblWait.Visible=false;
pgWait.Visible=false;
pnlHolderMain.Controls.Add(_pnlFlowClasses);
this._pnlFlowClasses.Visible = true;
this._pnlFlowClasses.Focus();
this.Refresh();
Application.DoEvents();
}
/// <summary>
/// Shows a new <see cref="frmAbout">about form</see>
/// </summary>
/// <param name="sender">btnAbout</param>
/// <param name="e">the event args</param>
private void btnAbout_Click(object sender, EventArgs e)
{
frmAbout fAbout = new frmAbout();
fAbout.ShowDialog(this);
}
/// <summary>
/// Shows a new <see cref="frmSettings">settings form</see>
/// </summary>
/// <param name="sender">btnSettings</param>
/// <param name="e">the event args</param>
private void btnSettings_Click(object sender, EventArgs e)
{
frmSettings fSettings = new frmSettings();
if (fSettings.ShowDialog(this) == DialogResult.OK)
{
if (isDrawableType())
{
DrawDiagram();
}
}
}
/// <summary>
/// The btnPrint menu clicked, so do a print
/// </summary>
/// <param name="sender">the btnPrint menu</param>
/// <param name="e">the event args</param>
private void btnPrint_Click(object sender, EventArgs e)
{
printDialog1.ShowNetwork=true;
//allow user to pick their printer
if (printDialog1.ShowDialog(this) == DialogResult.OK)
{
//get an image to print for the current diagram
Bitmap RtnBmp=getBitmapForControl();
//and now print it
if (RtnBmp != null)
{
_BitmapPrintPageEventHandler = new BitmapPrintPageEventHandler(RtnBmp);
this.printDocument1.PrintPage -= _BitmapPrintPageEventHandler.PrintPage;
this.printDocument1.PrintPage += _BitmapPrintPageEventHandler.PrintPage;
this.printDocument1.Print();
this.printDocument1.PrinterSettings=printDialog1.PrinterSettings;
}
else
{
Program.ErrorBox("There was a problem generating the image\r\n" +
"for the drawing control");
}
}
}
/// <summary>
/// Returns a Bitmap which represents the current diagram
/// </summary>
/// <returns>Bitmap which represents the current diagram</returns>
private Bitmap getBitmapForControl()
{
Cursor.Current = Cursors.WaitCursor;
int bmpSrcWidth = _pnlFlowClasses.MaxSize.Width;
int bmpSrcHeight = _pnlFlowClasses.MaxSize.Height;
//create a new ClassDrawerContainerPanel (which will not be shown on the form)
ClassDrawerContainerPanel pnl = new ClassDrawerContainerPanel();
_pnlFlowClasses.SuspendLayout();
_pnlFlowClasses.Visible = false;
this.Invalidate();
Rectangle newBounds = new Rectangle(0, 0, bmpSrcWidth, bmpSrcHeight);
pnl.Height = bmpSrcHeight;
pnl.Width = bmpSrcWidth;
pnl.Bounds = newBounds;
pnl.BackColor = Color.White;
pnl.SetBounds(0, 0, bmpSrcWidth, bmpSrcHeight);
pnl.ClassesToDraw = _pnlFlowClasses.ClassesToDraw;
pnl.LayoutControls();
Bitmap SrcBmp=null;
Bitmap RtnBmp=null;
try
{
SrcBmp = new Bitmap(bmpSrcWidth, bmpSrcHeight);
pnl.DrawToBitmap(SrcBmp, newBounds);
_pnlFlowClasses.ResumeLayout();
if (isDrawableType())
{
DrawDiagram();
}
RtnBmp = (Bitmap)SrcBmp.Clone();
}
catch (Exception)
{
if (SrcBmp != null) { SrcBmp.Dispose(); }
GC.Collect();
return null;
}
return RtnBmp;
}
/// <summary>
/// calls the SaveDiagram() method
/// </summary>
/// <param name="sender">btnSaveDiagram</param>
/// <param name="e">the event args</param>
private void btnSaveDiagram_Click(object sender, EventArgs e)
{
//create new save form
frmSave fSave = new frmSave();
if (fSave.ShowDialog(this) == DialogResult.OK)
{
//get the values from the save form
string filename = @fSave.SaveFile;
if (Directory.Exists(@fSave.SaveDirectory))
{
ImageFormat imgformat = fSave.RequiredImageFormat;
fSave.Dispose();
//GC.Collect();
Application.DoEvents();
//call the SaveTheDiagram method
if (SaveTheDiagram(filename, imgformat))
{
if (isDrawableType())
{
DrawDiagram();
}
_pnlFlowClasses.Visible = true;
this.Refresh();
Cursor.Current = Cursors.Default;
Program.InfoBox("Successfully saved the file \r\n" + filename);
}
else
{
Program.InfoBox("There was a problem saving the file \r\n" + filename);
}
}
else
{
Program.InfoBox("The save directory chosen \r\n" + fSave.SaveDirectory + "\r\n"
+ "does not exist. Please retry");
}
}
}
/// <summary>
/// Saves the entire contents of the pnlFlowClasses to an image, specified
/// by the input parameters
/// </summary>
/// <param name="filename">the filename to save the diagram to</param>
/// <param name="imgFormat">the image format to save the diagram as</param>
/// <returns></returns>
private bool SaveTheDiagram(string filename, ImageFormat imgFormat)
{
Cursor.Current = Cursors.WaitCursor;
int bmpSrcWidth = _pnlFlowClasses.MaxSize.Width;
int bmpSrcHeight = _pnlFlowClasses.MaxSize.Height;
//create a new ClassDrawerContainerPanel (which will not be shown on the form)
ClassDrawerContainerPanel pnl = new ClassDrawerContainerPanel();
_pnlFlowClasses.SuspendLayout();
Rectangle newBounds = new Rectangle(0, 0, bmpSrcWidth, bmpSrcHeight);
pnl.Height = bmpSrcHeight;
pnl.Width = bmpSrcWidth;
pnl.Bounds = newBounds;
pnl.BackColor = Color.White;
pnl.SetBounds(0, 0, bmpSrcWidth, bmpSrcHeight);
pnl.ClassesToDraw = _pnlFlowClasses.ClassesToDraw;
pnl.LayoutControls();
Bitmap SrcBmp=null;
Bitmap bmpNew = null;
Graphics gfx = null;
//save the image, however if we are saving the image to the save file name
//the Bitmap object maintains a lock on the pyhsical file, so we need to use
//another dummy Bitmap to hold the original image, so that the lock creates
//by the original image saving process can be released, then we can save
//the dummy Bitmap contents back to the orginal image and conduct the save.
//This is a well documented feature, see the following resources
//http://blog.vishalon.net/
//http://support.microsoft.com/?id=814675
try
{
SrcBmp = new Bitmap(bmpSrcWidth, bmpSrcHeight);
pnl.DrawToBitmap(SrcBmp, newBounds);
bmpNew = new Bitmap(SrcBmp.Width, SrcBmp.Height);
gfx = Graphics.FromImage(bmpNew);
gfx.DrawImage(SrcBmp, new Rectangle(0, 0, bmpNew.Width, bmpNew.Height),
0, 0, SrcBmp.Width, SrcBmp.Height, GraphicsUnit.Pixel);
// As original SrcBmp keeps lock on file, we need to copy the original image
// to a second image, and then release the lock on the image file. Of course,
// image from the initial image file is now existing on the new Graphics object
// which points to the second image, ready to copy back to the orinal image
SrcBmp.Dispose();
SrcBmp = bmpNew;
gfx.Dispose();
//do the save (now that the original lock has been dealt with)
SrcBmp.Save(filename, imgFormat);
SrcBmp.Dispose();
_pnlFlowClasses.ResumeLayout();
return true;
}
catch (Exception)
{
if (SrcBmp != null) { SrcBmp.Dispose(); }
if (bmpNew != null) { bmpNew.Dispose(); }
if (gfx != null) { gfx.Dispose(); }
GC.Collect();
return false;
}
}
/// <summary>
/// btnZoom button clicked, so so show zoom form
/// for the current image
/// </summary>
/// <param name="sender">btnZoom button</param>
/// <param name="e">the events args</param>
private void btnZoom_Click(object sender, EventArgs e)
{
frmZoom fZoom = new frmZoom();
Bitmap RtnBmp=getBitmapForControl();
if (RtnBmp != null)
{
fZoom.CurrentImage = RtnBmp;
fZoom.ShowDialog(this);
}
else
{
Program.ErrorBox("There was a problem generating the image\r\n" +
"for the drawing control");
}
}
#endregion
#region Paint
/// <summary>
/// Custom painted panel
/// </summary>
/// <param name="sender">pnlSettingsBanner</param>
/// <param name="e">the event args</param>
private void pnlTop_Paint(object sender, PaintEventArgs e)
{
//call base class
base.OnPaint(e);
//draw the rectangle
Graphics g = e.Graphics;
Pen p_Black = new Pen(Color.Black);
Rectangle rBounds = new Rectangle(0, 0, pnlTop.Size.Width,
pnlTop.Size.Height);
g.DrawRectangle(p_Black, rBounds);
Brush b_Divot = new HatchBrush(HatchStyle.Divot, Color.Gray, Color.Black);
g.FillRectangle(b_Divot, rBounds);
//draw the text
Brush b_White = new SolidBrush(Color.White);
Font fnt = new Font("Tahoma", 8, FontStyle.Bold);
// Draw string to screen.
g.DrawString("Current Assembly : " + _assembly.Name, fnt, b_White, new Point(5, 2));
}
#endregion
#endregion
}
#endregion
}