ResumeMaster : send resumes, logs them, etc.

Download resumemaster.zip

Synopsis:

resumemaster.xml
config.xml
logfile.xml
logfile.xsl
mainwindow.cs
CurrentProcess.cs
App.cs
EmailPackage.cs
FileSaver.cs
Globals.cs
GridPanel.cs
PanelGeometry.cs
PanelWithLabelsGeometry.cs
State.cs
StateMain.cs
StateResumeConfirm.cs
StateResumeEditBody.cs
StateResumeStart.cs
WizardButtons.cs
XmlLog.cs


resumemaster.xml

Synopsis
<ini>
  <!--A copy of the email will be sent to this address: -->
  <DefaultBccAddress>john.resume@arrizza.com</DefaultBccAddress>
  
  <!--The address the email comes from (i.e. yours): -->
  <DefaultFromAddress>john.resume@arrizza.com</DefaultFromAddress>

  <!--The default address the email goes to. Make this one of your own 
      for saftey's sake. When you send an email, you have to 
      overwrite it anyway. -->
  <DefaultToAddress>john@arrizza.com</DefaultToAddress> 
  
  <!--Your title-->
  <DefaultTitle>Lead Software Engineer</DefaultTitle>
  
  <!-- The text that will appear in the subject line of the email -->
  <DefaultSubjectLine>resume for John Arrizza</DefaultSubjectLine>
  
  <!-- the resume to attach. Can be any file or type -->
  <DefaultResumeFilename>d:\mystuff\myresume\johnarrizzaresume.doc</DefaultResumeFilename>
  
  <!-- the template used for the email body. -->
  <DefaultTemplateFilename>d:\mystuff\myresume\coverletter.jmt</DefaultTemplateFilename>
  
  <!-- The default salutation to use. -->
  <DefaultSalutation>Sir or Madam</DefaultSalutation>

  <!-- Whether or not to include the salary reqm'ts text -->
  <DefaultIncludeSalaryRequirements>false</DefaultIncludeSalaryRequirements>

  <!-- the default text for salary requirements.
  Any embedded newlines are removed.
  use '\n' to put a newline into the text -->
  <DefaultSalaryRequirementsText>
\nI want a million dollars. 
However, in terms of Salary History my latest wage was %CURRENTSALARY%.
  </DefaultSalaryRequirementsText>

 <!-- the default text of the signature text -->
  <DefaultSignatureText>
Jump like a rabbit.\n
Sleep like a log.\n
Code like a monkey.
  </DefaultSignatureText>
  
  <!-- your current salary -->
  <DefaultCurrentSalary>1234</DefaultCurrentSalary>
</ini>

config.xml

Synopsis
<config>
   <!-- these two entries must be present -->
   <state id='main' class='resumemastercore.StateNodes.Main' />
   <trans from='main' action='next:1' next='resumestart' />

   <state id='resumestart' class='resumemastercore.StateNodes.Resume.Start' />
   <state id='resumeeditbody' class='resumemastercore.StateNodes.Resume.EditBody' />
   <state id='resumeconfirm' class='resumemastercore.StateNodes.Resume.Confirm' />
   
   <trans from='resumestart' action='cancel' next='main' />
   <trans from='resumestart' action='previous' next='main' />
   <trans from='resumestart' action='next' next='resumeeditbody' />
   <trans from='resumestart' action='finish' next='resumeconfirm' />
   <trans from='resumeeditbody' action='cancel' next='main' />
   <trans from='resumeeditbody' action='previous' next='resumestart' />
   <trans from='resumeeditbody' action='next' next='resumeconfirm' />
   <trans from='resumeeditbody' action='finish' next='resumeconfirm' />
   <trans from='resumeconfirm' action='cancel' next='main' />
   <trans from='resumeconfirm' action='previous' next='resumeeditbody' />
   <trans from='resumeconfirm' action='finish' next='main' />
</config>

logfile.xml

Synopsis
<?xml version="1.0"?>
<?xml-stylesheet href="logfile.xsl" type="text/xsl"?>
<!DOCTYPE logfile [ <!ENTITY thelog SYSTEM "log.txt"> ]>

<log>
&thelog;
</log>




logfile.xsl

Synopsis
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="html" />

<!--  -->
<xsl:template match="/">
<html>
  <head>
    <title>Log</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  </head>

  <body>
    <h1>Log of Emails Sent</h1>
    <p><strong>
    Total emails sent: <xsl:value-of select="count(log/email)" />
    </strong></p>
    <p>
      <table border="1" cellpadding="4" cellspacing="4">
        <tr style="background: lightgreen; text-align:center; text-size: bold">
          <td>Date</td>
          <td>Company</td>
          <td>Website</td>
          <td>Email</td>
          <td>Notes</td>
          <td>CoverLetter</td>
          <td>Resume</td>
        </tr>
        <xsl:apply-templates select="log/email" />
      </table>
    </p>
  </body>
</html>
</xsl:template>

<!--  -->
<xsl:template match="email">
  <tr>
    <td>
     <xsl:value-of select="date" />
    </td>
    <td>
     <xsl:choose>
      <xsl:when test="companyname">
         <xsl:value-of select="companyname" />
      </xsl:when>
      <xsl:otherwise>unknown</xsl:otherwise>
    </xsl:choose>
    </td>
    <td style="text-align:center">
      <a>
        <xsl:attribute name="href">
            <xsl:if test="not(contains(website, 'http://'))">
              <xsl:text>http://</xsl:text>
            </xsl:if>
            <xsl:value-of select="website" />
        </xsl:attribute>
        <xsl:text>*</xsl:text>
      </a>
    </td>
    <td style="text-align:center">
      <a>
        <xsl:attribute name="href">
            <xsl:text>mailto:</xsl:text>
            <xsl:value-of select="toaddress" />
        </xsl:attribute>
        <xsl:text>*</xsl:text>
      </a>
    </td>
    <td>
     <xsl:value-of select="notes" />
    </td>
    <td style="text-align:center">
      <a>
        <xsl:attribute name="href">
            <xsl:text>coverletters/</xsl:text>
            <xsl:value-of select="savedcoverletterfilename" />
        </xsl:attribute>
        <xsl:text>*</xsl:text>
      </a>
    </td>
    <td style="text-align:center">
      <a>
        <xsl:attribute name="href">
            <xsl:text>resumes/</xsl:text>
            <xsl:value-of select="savedresumefilename" />
        </xsl:attribute>
        <xsl:text>*</xsl:text>
      </a>
    </td>

  </tr>
</xsl:template>


<!--
<email>
  <date>8/9/2002 11:15:27 AM</date>
  <toaddress>john@arrizza.com</toaddress>
  <fromaddress>john.resume@arrizza.com</fromaddress>
  <originalresumefilename>d:\mystuff\myresume\johnarrizzaresume.doc</originalresumefilename>
  <originalcoverlettertemplate>d:\mystuff\myresume\flipdogcoverletter.jmt</originalcoverlettertemplate>
  <bodyformat>text</bodyformat>
  <contactname>Sir or Madam</contactname>
  <companyname>none</companyname>
  <website>none</website>
  <notes>none</notes>
  <savedcoverletterfilename>20020809_1527.txt</savedcoverletterfilename>
  <savedresumefilename>20020809_1527.doc</savedresumefilename>
</email>
-->

</xsl:stylesheet>


mainwindow.cs

Synopsis
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using resumemastercore;

namespace resumemaster
{
  /// <summary>
  /// Summary description for MainWindow.
  /// </summary>
  public class MainWindow : System.Windows.Forms.Form
  {
    private App mApp;

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    public MainWindow()
    {
      mApp = new App(this);
      InitializeComponent();
    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if (components != null) 
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

		#region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.SuspendLayout();

      // 
      // MainWindow
      // 
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(680, 525);
      this.Name = "MainWindow";
      this.Text = "ResumeMaster";
      this.Layout += new System.Windows.Forms.LayoutEventHandler(this.mMainWindow_HandleLayout);
      this.ResumeLayout(false);

    }
		#endregion

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() 
    {
      Application.Run(new MainWindow());
    }
    private void mMainWindow_HandleLayout(object sender, LayoutEventArgs e)
    {
      mApp.HandleLayout();
      Invalidate();
    }

  }
}

CurrentProcess.cs

Synopsis
using System;
using System.Runtime.InteropServices;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for WinProcess.
  /// </summary>
  public class CurrentProcess
  {
    internal delegate string getcmdlineDelegate();
    internal static getcmdlineDelegate func = new getcmdlineDelegate(getcmdline);
    
    public static string StartupDirectory
    {
      get 
      {
        string commandLine = func().Trim();
        if (commandLine[0] == '\"')
        {
          commandLine = commandLine.Substring(1, commandLine.IndexOf("\"", 1));
        }
        else
        {
          int ispace = commandLine.IndexOf(" ");
          if (ispace > 0)
            commandLine = commandLine.Substring(0, ispace);
        }
        return commandLine.Substring(0, commandLine.LastIndexOf('\\'));
      }
    }

    [ DllImport( "Kernel32.dll", CharSet=CharSet.Auto )] 
    private static extern IntPtr GetCommandLine();   
    internal static string getcmdline()
    {
      return Marshal.PtrToStringAuto( GetCommandLine() );
    }
  }
}

App.cs

Synopsis
using System;
using System.Windows.Forms;
using System.Collections;
using System.Reflection;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for Class1.
  /// </summary>
  public class App
  {
    private Hashtable mNextStateTable;
    private Hashtable mStateIdToClassTable;
    private WizardButtons mButtons;
    private StateNodes.State mState;
    private Form mParent;

    public App(Form parent)
    {
      Globals.init();
      mParent = parent;
      mButtons = new WizardButtons(mParent, this);

      BuildTables();

      SetNextState("main");
    }
    public void HandleLayout()
    {
      if (mState == null) return;
      mState.Layout();
    }
    public void Button_Click(object sender, System.EventArgs e)
    {
      NextState((Button)sender);
    }
    public void NextState(Button btn)
    {
      HandleAction(btn);

      SetNextState((string)mNextStateTable[GetCurrentStateKey(btn)]);
    }
    private string GetCurrentStateKey(Button btn)
    {
      return  mState.Id + ":" + mButtons.Name(btn) + (mState.Id.Equals("main") ? ":" + mState.Modifier : "");
    }
    private void SetNextState(string nextstate)
    {
      if (nextstate != null) 
        mState = CreateState(nextstate);
      mState.Entering();
    }
    private void HandleAction(Button btn)
    {
      if (mState == null) return;

      if (mButtons.IsNext(btn)) 
        mState.OnNext();
      else if (mButtons.IsPrevious(btn)) 
        mState.OnPrevious();
      else if (mButtons.IsFinish(btn))
        mState.OnFinish();
      else if (mButtons.IsCancel(btn))
        mState.OnCancel();

      mState.Leaving();
    }
    private StateNodes.State CreateState(string name)
    {
      string classname = (string) mStateIdToClassTable[name];
      if (classname == null) return null;

      Assembly a = Assembly.GetCallingAssembly();
      Type[] parmtypes = {a.GetType("resumemastercore.StateNodes.State"), new Form().GetType(), a.GetType("resumemastercore.WizardButtons")};
      object[] parms = {mState, mParent, mButtons};
      return (StateNodes.State) a.GetType(classname).GetConstructor(parmtypes).Invoke(parms);
    }

    private void BuildTables()
    {
      mNextStateTable = new Hashtable();
      mStateIdToClassTable = new Hashtable();

      SimpleXml.SimpleXmlWithAttributesReader reader = new SimpleXml.SimpleXmlWithAttributesReader();
      reader.RootTag = "config";
      reader.Add("state", new SimpleXml.AttributeHandler(OnStateDefinition));
      reader.Add("trans", new SimpleXml.AttributeHandler(OnTransition));
      reader.load(CurrentProcess.StartupDirectory + @"\config.xml");
    }

    private void OnStateDefinition(string tag, Hashtable attrs)
    {
      if (!tag.Equals("state")) return;

      //e.g.  <state id="resumestart" class="resumemastercore.StateNodes.Resume.Start" />
      if (attrs["id"] == null) throw new Exception("missing 'id' attribute in config.xml");
      if (attrs["class"] == null) throw new Exception("missing 'class' attribute in config.xml");

      mStateIdToClassTable[attrs["id"]] = attrs["class"];
    }
    private void OnTransition(string tag, Hashtable attrs)
    {
      if (!tag.Equals("trans")) return;
      //e.g.  <trans from="resumestart" action="cancel" next="main" />

      if (attrs["from"] == null) throw new Exception("missing 'from' attribute in config.xml");
      if (attrs["action"] == null) throw new Exception("missing 'action' attribute in config.xml");
      if (attrs["next"] == null) throw new Exception("missing 'next' attribute in config.xml");

      mNextStateTable[(string)attrs["from"] + ":" + (string)attrs["action"]]= attrs["next"];
    }
  }
}

EmailPackage.cs

Synopsis
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Web.Mail;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for Resume.
  /// </summary>
  public class EmailPackage
  {
    private string mFromAddress;
    private string mToAddress;
    private string mSubjectLine;
    private string mSalutation;
    private string mTitle;
    private bool mIncludeSalaryRequirements;
    private string mSalaryRequirementsText;
    private int mCurrentSalary;
    private string mTemplateFilename;
    private string mResumeFilename;
    private bool mAttachResume;
    private string mSignatureText;
    private string mBody;
    private MailFormat mBodyFormat = MailFormat.Text;
    private string mContactName = null;
    private string mCompanyName = null;
    private string mWebSite = null;
    private string mNotes = null;
    private string mSavedResumeFilename;
    private string mSavedCoverLetterFilename;

    public EmailPackage()
    {
      mIncludeSalaryRequirements = Globals.DefaultIncludeSalaryRequirements;
      mCurrentSalary = Globals.DefaultCurrentSalary;
      mSalaryRequirementsText = Globals.DefaultSalaryRequirementsText;
      mSignatureText = Globals.DefaultSignatureText;
      mAttachResume = false;
    }
    public string FromAddress
    {
      set
      {
        mFromAddress = value;
      }
      get
      {
        if (mFromAddress == null) mFromAddress = Globals.DefaultFromAddress;
        return mFromAddress;
      }
    }
    public string ToAddress
    {
      set
      {
        mToAddress = value;
      }
      get
      {
        if (mToAddress == null) mToAddress = Globals.DefaultToAddress;
        return mToAddress;
      }
    }
    public string Title
    {
      set
      {
        mTitle = value;
      }
      get
      {
        if (mTitle == null) mTitle = Globals.DefaultTitle;
        return mTitle;
      }
    }
    public string SubjectLine
    {
      set
      {
        mSubjectLine = value;
      }
      get
      {
        if (mSubjectLine == null) mSubjectLine = Globals.DefaultSubjectLine;
        return mSubjectLine;
      }
    }
    public string Salutation
    {
      set
      {
        mSalutation = value;
      }
      get
      {
        if (mSalutation == null) mSalutation = Globals.DefaultSalutation;
        return mSalutation;
      }
    }
    public bool IncludeSalaryRequirements
    {
      set
      {
        mIncludeSalaryRequirements = value;
      }
      get
      {
        
        return mIncludeSalaryRequirements;
      }
    }
    public string TemplateFilename
    {
      set
      {
        mTemplateFilename = value;
      }
      get
      {
        if (mTemplateFilename == null) mTemplateFilename = Globals.DefaultTemplateFilename;
        return mTemplateFilename;
      }
    }
    public string ResumeFilename
    {
      set
      {
        mResumeFilename = value;
      }
      get
      {
        if (mResumeFilename == null) mResumeFilename = Globals.DefaultResumeFilename;
        return mResumeFilename;
      }
    }
    public bool AttachResume
    {
      set
      {
        mAttachResume = value;
      }
      get
      {
        return mAttachResume;
      }
    }
    public string Body
    {
      set
      {
        mBody = value;
      }
      get
      {
        if (mBody == null) mBody = BuildBody();
        return mBody;
      }
    }
    public string BodyFormat
    {
      get
      {
        if (mBodyFormat == MailFormat.Text)
          return "text";
        if (mBodyFormat == MailFormat.Html)
          return "html";
        return "unknown";
      }
    }
    public string ContactName
    {
      set
      {
        mContactName = value;
      }
      get
      {
        if (mContactName == null)
        {
          if (mSalutation == null) return Globals.DefaultSalutation;
          return mSalutation;
        }

        return mContactName;
      }
    }
    public string WebSite
    {
      set
      {
        mWebSite = value;
      }
      get
      {
        if (mWebSite == null) return "none";
        return mWebSite;
      }
    }
    public string CompanyName
    {
      set
      {
        mCompanyName = value;
      }
      get
      {
        if (mCompanyName == null) return "none";
        return mCompanyName;
      }
    }
    public string Notes
    {
      set
      {
        mNotes = value;
      }
      get
      {
        if (mNotes == null) return "none";
        return mNotes;
      }
    }
    public string SavedResumeFilename
    {
      get {return mSavedResumeFilename; }
    }
    public string SavedCoverLetterFilename
    {
      get {return mSavedCoverLetterFilename; }
    }
    public void Send()
    {
      MailMessage mail = new MailMessage();
      mail.From = this.FromAddress;
      mail.To = this.ToAddress;
      mail.Bcc = Globals.DefaultBccAddress;
      mail.Subject = this.SubjectLine;
      mail.BodyFormat = mBodyFormat;
      mail.Body = this.Body;

      if (this.AttachResume && mResumeFilename != null)
        mail.Attachments.Add(new MailAttachment(mResumeFilename));

      SmtpMail.Send(mail);
      SaveFiles();
      WriteLog();
    }
    private void SaveFiles()
    {
      FileSaver fs = new FileSaver();
      mSavedCoverLetterFilename = fs.saveCoverLetter(this.Body);
      mSavedResumeFilename = fs.saveResume(mResumeFilename);
    }
    private void WriteLog()
    {
      XmlLog l = new XmlLog();
      l.WriteEntry(this);
    }
    private bool hasMacro(StringBuilder s)
    {
      for(int i = 0; i < s.Length; ++i)
      {
        if (s[i] == '%') return true;
      }
      return false;
    }
    private string BuildBody()
    {
      TextReader reader = new StreamReader(mTemplateFilename);
      StringBuilder s = new StringBuilder(reader.ReadToEnd());
      while(hasMacro(s))
      {
        s = s.Replace("%MYTITLE%", this.Title);
        s = s.Replace("%SALUTATION%", this.Salutation);
        BuildSalaryExpecations(s);
        s = s.Replace("%SIGNATURE%", this.mSignatureText);
      }

      return s.ToString();
    }
    private void BuildSalaryExpecations(StringBuilder s)
    {
      if (this.IncludeSalaryRequirements)
      {
        string salarytext = mSalaryRequirementsText;
        string currsalary = String.Format("${0:N} per year", mCurrentSalary);
        salarytext = salarytext.Replace("%CURRENTSALARY%", currsalary);
        s = s.Replace("%SALARYEXPECTATIONS%", salarytext);
      }
      else
        s = s.Replace("%SALARYEXPECTATIONS%", "");
    }
  }
}

FileSaver.cs

Synopsis
using System;
using System.Security.Cryptography;
using System.IO;
using System.Collections;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for FileSaver.
  /// </summary>
  public class FileSaver
  {
    private readonly string cCoverLetterDir = "coverletters";
    private readonly string cResumeDir = "resumes";
    private string mRootDir;

    public FileSaver()
    {
      mRootDir = CurrentProcess.StartupDirectory;
    }
    //coverletter contents are hashed via MD5 and compared against
    //each coverletter in the \coverletters\ directory. If it's new, it is saved
    //with a unique file name. If it's old, the name is saved
    //ditto for resumes.
    public string saveCoverLetter(string text)
    {
      byte[] bytes = StringToByteArray(text);
      string dir = mRootDir + @"\" + cCoverLetterDir;

      if (!Directory.Exists(dir))
      {
        Directory.CreateDirectory(dir);
      }
      else
      {
        string file = SearchExistingFiles(dir, bytes);
        if (file != null) return file;
      }

      return GenerateNewFile(dir, ".txt", bytes);
    }
    public string saveResume(string filename)
    {
      string dir = mRootDir + @"\" + cResumeDir;

      if (!Directory.Exists(dir))
      {
        Directory.CreateDirectory(dir);
      }
      else
      {
        string file = SearchExistingFiles(dir, filename);
        if (file != null) return file;
      }

      return GenerateNewFile(dir, filename);
    }

    internal byte[] StringToByteArray(string s)
    {
      char[] schars = s.ToCharArray();
      byte[] sbytes = new byte[s.Length];
      for(int i = 0; i < s.Length; ++i)
        sbytes[i] = Convert.ToByte(schars[i]);
      return sbytes;
    }

    internal string GenerateNewFile(string dir, string filename)
    {
      string extension = filename.Substring(filename.LastIndexOf("."));
      string file = DateTime.Now.ToString("yyyyMMdd_mmss") + extension;
      File.Copy(filename, dir + @"\" + file);
      return file;
    }

    internal string GenerateNewFile(string dir, string extension, byte[] bytes)
    {
      string file = DateTime.Now.ToString("yyyyMMdd_mmss") + extension;
      FileStream fs = new FileStream(dir + @"\" + file, FileMode.Create, FileAccess.Write);
      fs.Write(bytes, 0, bytes.Length);
      fs.Close();

      return file;
    }

    internal string SearchExistingFiles(string dir, string filename)
    {
      MD5 md5 = new MD5CryptoServiceProvider();
      FileStream fs1 = new FileStream(filename, FileMode.Open, FileAccess.Read);
      byte[] hash = md5.ComputeHash(fs1);
      fs1.Close();

      string[] files = Directory.GetFiles(dir);
      foreach (string file in files)
      {
        FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
        if (HashesAreEqual(hash, md5.ComputeHash(fs)))
        {
          fs.Close();
          return file.Substring(file.LastIndexOf('\\') + 1);
        }
        fs.Close();
      }
      return null;
    }

    internal string SearchExistingFiles(string dir, byte[] bytes)
    {
      MD5 md5 = new MD5CryptoServiceProvider();
      byte[] hash = md5.ComputeHash(bytes);

      string[] files = Directory.GetFiles(dir);
      foreach (string file in files)
      {
        FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
        if (HashesAreEqual(hash, md5.ComputeHash(fs)))
        {
          fs.Close();
          return file.Substring(file.LastIndexOf('\\') + 1);
        }
        fs.Close();
      }
      return null;
    }

    internal bool HashesAreEqual(byte[] hash1, byte[] hash2)
    {
      if (hash1 == null && hash2 == null) return true;
      if (hash1 == null ^ hash2 == null) return false;
      if (hash1.Length != hash2.Length) return false;
      for(int i = 0; i < hash1.Length; ++i)
      {
        if (!hash1.GetValue(i).Equals(hash2.GetValue(i)))
          return false;
      }
      return true;
    }
  }
}

Globals.cs

Synopsis
using System;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for Globals.
  /// </summary>
  public class Globals
  {
    public static readonly int vGap = 7;
    public static readonly int hGap = 4;

    public static string DefaultBccAddress;
    public static string DefaultFromAddress;
    public static string DefaultToAddress;
    public static string DefaultTitle;
    public static string DefaultSubjectLine;
    public static string DefaultResumeFilename;
    public static string DefaultTemplateFilename;
    public static string DefaultSalutation;
    public static bool DefaultIncludeSalaryRequirements;
    public static string DefaultSalaryRequirementsText;
    public static string DefaultSignatureText; 
    public static int DefaultCurrentSalary;

    public static void init()
    {
      SimpleXml.SimpleXmlReader reader = new SimpleXml.SimpleXmlReader();
      reader.RootTag = "ini";
      reader.AddDefault(new SimpleXml.ElementHandler(textdelegate));
      reader.load(CurrentProcess.StartupDirectory + "/resumemaster.xml");
    }

    private static void textdelegate(string tag, string val)
    {
      val = val.Replace("\n", "");
      val = val.Replace("\r", "");
      val = val.Replace("\\n", Environment.NewLine);
      switch (tag)
      {
        case "defaultbccaddress" : DefaultBccAddress = val; break;
        case "defaultfromaddress" :  DefaultFromAddress = val; break;
        case "defaulttoaddress" :  DefaultToAddress = val; break;
        case "defaulttitle" :  DefaultTitle = val; break;
        case "defaultsubjectline" :  DefaultSubjectLine = val; break;
        case "defaultresumefilename" :  DefaultResumeFilename = val; break;
        case "defaulttemplatefilename" :  DefaultTemplateFilename = val; break;
        case "defaultsalutation" :  DefaultSalutation = val; break;
        case "defaultincludesalaryrequirements" :  DefaultIncludeSalaryRequirements = bool.Parse(val); break;
        case "defaultsalaryrequirementstext" :  DefaultSalaryRequirementsText = val; break;
        case "defaultsignaturetext" :   DefaultSignatureText   = val; break;
        case "defaultcurrentsalary" :  DefaultCurrentSalary = int.Parse(val); break;
      }
    }
  }
}

GridPanel.cs

Synopsis
using System;
using System.Windows.Forms;

namespace resumemastercore
{
	/// <summary>
	/// Summary description for GridPanel.
	/// </summary>
	public class GridPanel : System.Windows.Forms.Panel
	{
    protected override void OnLayout(LayoutEventArgs e)
    {
      base.OnLayout(e);
    }
    public void StartLayout(Form parent, WizardButtons buttons)
    {
      this.SuspendLayout();
      int btnspace = buttons.Height + Globals.vGap;
      this.Location = new System.Drawing.Point(Globals.hGap, btnspace);
      this.Size = new System.Drawing.Size(parent.ClientSize.Width - 2 * Globals.hGap, parent.ClientSize.Height - btnspace);
      this.TabIndex = 0;
      this.BorderStyle = BorderStyle.FixedSingle;
    }
    public void FinishLayout(Form parent)
    {
      parent.Controls.Add(this);
      this.Visible = true;
      this.ResumeLayout(false);
    }

    public void Leaving()
    {
      this.Visible = false;
    }
  }
}

PanelGeometry.cs

Synopsis
using System;
using System.Windows.Forms;
using System.Drawing;

namespace resumemastercore
{
  class PanelGeometry
  {
    protected int width;
    protected int height = 23;

    private GridPanel mPanel;
    int vLocation;
    int hLocation;
    int tabindex;
    private readonly int column0width = 120;

    public PanelGeometry(GridPanel panel)
    {
      mPanel = panel;
      GotoRow0();
      tabindex = 0;
    }
    public void AddLabel(string text)
    {
      GotoColumn0();
      NewLabel(text, ContentAlignment.MiddleRight);
    }
    public void AddLabelCol1(string text)
    {
      GotoColumn1();
      NewLabel(text, ContentAlignment.MiddleLeft);
      GotoNextRow();
    }
    public void AddTextBox(TextBox tbox, string tboxname, string text)
    {
      GotoColumn1();
      NewTextBox(tbox, tboxname, text);
      GotoNextRow();
      tabindex++;
    }
    public void AddCheckBox(CheckBox chbox, string chboxname, string text, bool initialvalue)
    {
      GotoColumn1();
      NewCheckBox(chbox, chboxname, text, initialvalue);
      GotoNextRow();
      tabindex++;
    }
    protected void NewLabel(string text, ContentAlignment align)
    {
      Label label = new Label();
      label.TextAlign = align;
      NewControl(label, text);
    }
    private void NewTextBox(TextBox tbox, string tboxname, string text)
    {
      tbox.Name = tboxname;
      tbox.TextAlign = HorizontalAlignment.Left;
      NewControl(tbox, text);
    }
    private void NewCheckBox(CheckBox chbox, string chboxname, string text, bool initialvalue)
    {
      chbox.Name = chboxname;
      chbox.TextAlign = ContentAlignment.MiddleLeft;
      chbox.Checked = initialvalue;
      NewControl(chbox, text);
    }
    private void NewControl(Control control, string text)
    {
      control.Location = new System.Drawing.Point(hLocation, vLocation);
      control.Size  = new System.Drawing.Size(width, height);
      control.Text = text;
      mPanel.Controls.Add(control);
    }
    private void GotoRow0()
    {
      vLocation = Globals.vGap;
    }
    protected void GotoNextRow()
    {
      vLocation += height + Globals.vGap;
    }
    protected void GotoColumn0()
    {
      hLocation = Globals.hGap;
      width = column0width;
    }
    private void GotoColumn1()
    {
      hLocation = Globals.hGap + column0width + Globals.hGap;
      width = RemainingHorizontalSpace();
    }
    protected int RemainingHorizontalSpace()
    {
      return mPanel.ClientSize.Width - hLocation - Globals.hGap;
    }
  }

}

PanelWithLabelsGeometry.cs

Synopsis
using System;
using System.Windows.Forms;
using System.Drawing;

namespace resumemastercore
{
  class PanelWithLabelsGeometry : PanelGeometry
  {
    public PanelWithLabelsGeometry(GridPanel panel)
      : base(panel)
    {
      height = 13;
    }
    public void AddWholeRowLabel(string text)
    {
      GotoColumn0();
      width = RemainingHorizontalSpace();
      NewLabel(text, ContentAlignment.MiddleLeft);
      GotoNextRow();
    }
    public void SkipRows(int num)
    {
      for(int i = 0; i < num; ++i)
        GotoNextRow();
    }
  }
}

State.cs

Synopsis
using System;
using System.Windows.Forms;

namespace resumemastercore.StateNodes
{
  /// <summary>
  /// Summary description for State.
  /// </summary>
  public abstract class State
  {
    protected Form mParent;
    protected string mId;
    protected int mModifier;
    protected GridPanel mPanel;
    protected WizardButtons mButtons;
    protected object mPersistent;

    public State(State prevState, Form parent, WizardButtons buttons)
    {
      mPersistent = prevState == null ? null : prevState.mPersistent;
      mParent = parent;
      mButtons = buttons;

      mPanel = new GridPanel();
      mPanel.StartLayout(mParent, mButtons);
    }
    public virtual void OnNext()
    {
    }
    public virtual void OnPrevious()
    {
    }
    public virtual void OnFinish()
    {
    }
    public virtual void OnCancel()
    {
    }
    public virtual void Leaving()
    {
      mPanel.Leaving();
    }
    public virtual void Entering()
    {
      mPanel.FinishLayout(mParent);
    }
    public string Id
    {
      get
      {
        return mId;
      }
    }
    public virtual int Modifier
    {
      get
      {
        return mModifier;
      }
    }
    public virtual void Layout()
    {
      mPanel.StartLayout(mParent, mButtons);
    }
  }
}

StateMain.cs

Synopsis
using System;
using System.Windows.Forms;
using resumemastercore.StateNodes;

namespace resumemastercore.StateNodes
{
  /// <summary>
  /// Summary description for StateMain.
  /// </summary>
  public class Main : State
  {
    private CheckBox mSendResume;
    public Main(State prev, Form parent, WizardButtons buttons)
      : base(prev, parent, buttons)
    {
      buttons.Set(false, true, false, false);
      mId = "main";
      mPanel.Name = mId;
      mSendResume = new CheckBox();
    }
    public override void Entering()
    {
      this.mSendResume.Location = new System.Drawing.Point(Globals.hGap, Globals.vGap);
      this.mSendResume.Name = "mSendResume";
      this.mSendResume.Size = new System.Drawing.Size(96, 23);
      this.mSendResume.TabIndex = 0;
      this.mSendResume.Text = "Send Resume";
      
      mPanel.Controls.Add(mSendResume);
      
      base.Entering();
    }
    public override int Modifier
    {
      get
      {
        if (mSendResume.Checked) return 1;
        return base.Modifier;
      }
    }
    public override void OnNext()
    {
      this.mPersistent = null;
    }
  }
}

StateResumeConfirm.cs

Synopsis
using System;
using System.Windows.Forms;
using resumemastercore.StateNodes;

namespace resumemastercore.StateNodes.Resume
{
  /// <summary>
  /// Summary description for StateResumeStart.
  /// </summary>
  public class Confirm : State
  {
    public Confirm(State prev, Form parent, WizardButtons buttons)
      : base(prev, parent, buttons)
    {
      mButtons.Set(true, false, true, true);
      mId = "resumeconfirm";
      mPanel.Name = mId;
    }
    public override void OnFinish()
    {
      ((EmailPackage) mPersistent).Send();
    }

    public override void Entering()
    {
      EmailPackage pkg = (EmailPackage) mPersistent;

      PanelWithLabelsGeometry pg = new PanelWithLabelsGeometry(mPanel);

      pg.AddLabel("From Address:");
      pg.AddLabelCol1(pkg.FromAddress);

      pg.AddLabel("To Address:");
      pg.AddLabelCol1(pkg.ToAddress);

      pg.AddLabel("Subject Line:");
      pg.AddLabelCol1(pkg.SubjectLine);

      pg.AddLabel("Resume to send:");
      pg.AddLabelCol1(pkg.ResumeFilename);

      pg.AddLabel("Salary Requirements: ");
      pg.AddLabelCol1(pkg.IncludeSalaryRequirements ? "included" : "not included");

      pg.SkipRows(5);
      pg.AddWholeRowLabel("Press 'Finish' to send the email immediately and save information to the log.");
      pg.AddWholeRowLabel("Press 'Cancel' to not send the email. No information is saved in the log.");
      base.Entering();
    }
  }
}

StateResumeEditBody.cs

Synopsis
using System;
using System.Windows.Forms;
using resumemastercore.StateNodes;

namespace resumemastercore.StateNodes.Resume
{
  /// <summary>
  /// Summary description for StateResumeEditBody.
  /// </summary>
  public class EditBody : State
  {
    TextBox mBody;

    public EditBody(State prev, Form parent, WizardButtons buttons)
      : base(prev, parent, buttons)
    {
      mButtons.Set(true, true, true, true);
      mId = "resumeeditbody";
      mPanel.Name = mId;
      mBody = new TextBox();
    }
    public override void Layout()
    {
      int width = mPanel.ClientSize.Width - 2 * Globals.hGap;
      int height = mPanel.ClientSize.Height - 2 * Globals.vGap;
      this.mBody.Size = new System.Drawing.Size(width, height);
      base.Layout();
    }
    public override void Entering()
    {
      int width = mPanel.ClientSize.Width - 2 * Globals.hGap;
      int height = mPanel.ClientSize.Height - 2 * Globals.vGap;

      this.mBody.Location = new System.Drawing.Point(Globals.hGap, Globals.vGap);
      this.mBody.Name = "mBody";
      this.mBody.Size = new System.Drawing.Size(width, height);
      this.mBody.TabIndex = 0;
      this.mBody.Text = ((EmailPackage) mPersistent).Body;
      this.mBody.Multiline = true;
      this.mBody.AcceptsTab = true;
      mPanel.Controls.Add(mBody);

      base.Entering();
    }
    public override void OnNext()
    {
      ((EmailPackage) mPersistent).Body = mBody.Text;
    }
  }
}

StateResumeStart.cs

Synopsis
using System;
using System.Windows.Forms;
using System.Drawing;
using resumemastercore.StateNodes;

namespace resumemastercore.StateNodes.Resume
{
  /// <summary>
  /// Summary description for StateResumeStart.
  /// </summary>
  public class Start : State
  {
    TextBox mSubjectLine = new TextBox();
    TextBox mFromAddress = new TextBox();
    TextBox mToAddress = new TextBox();
    TextBox mResumeFilename = new TextBox();
    TextBox mCoverLetterTemplateFilename = new TextBox();
    TextBox mSalutation = new TextBox();
    TextBox mContactName = new TextBox();
    TextBox mCompanyName = new TextBox();
    TextBox mNotes = new TextBox();
    TextBox mWebSite = new TextBox();
    CheckBox mIncludeSalaryRequirements  = new CheckBox();
    CheckBox mAttachResume = new CheckBox();

    public Start(State prev, Form parent, WizardButtons buttons)
      : base(prev, parent, buttons)
    {
      mButtons.Set(true, true, true, true);
      mId = "resumestart";
      mPanel.Name = mId;
      if (mPersistent == null)
        mPersistent = new EmailPackage();
    }
    
    public override void Entering()
    {
      EmailPackage pkg = (EmailPackage) mPersistent;

      PanelGeometry pg = new PanelGeometry(mPanel);

      pg.AddLabel("Company Name:");
      pg.AddTextBox(mCompanyName, "mCompanyName", pkg.CompanyName);

      pg.AddLabel("To Address:");
      pg.AddTextBox(mToAddress, "mToAddress", pkg.ToAddress);

      pg.AddLabel("Salutation:");
      pg.AddTextBox(mSalutation, "mSalutation", pkg.Salutation);

      pg.AddLabel("Contact Name:");
      pg.AddTextBox(mContactName, "mContactName", pkg.ContactName);

      pg.AddLabel("Web site:");
      pg.AddTextBox(mWebSite, "mWebSite", pkg.WebSite);

      pg.AddLabel("Notes:");
      pg.AddTextBox(mNotes, "mNotes", pkg.Notes);

      pg.AddLabel("From Address:");
      pg.AddTextBox(mFromAddress, "mFromAddress", pkg.FromAddress);

      pg.AddLabel("Subject Line:");
      pg.AddTextBox(mSubjectLine, "mSubjectLine", pkg.SubjectLine);

      pg.AddLabel("Resume to send:");
      pg.AddTextBox(mResumeFilename, "mResumeFilename", pkg.ResumeFilename);

      pg.AddLabel("Cover Letter Template:");
      pg.AddTextBox(mCoverLetterTemplateFilename, "mCoverLetterTemplateFilename", pkg.TemplateFilename);

      pg.AddCheckBox(mIncludeSalaryRequirements, "mIncludeSalaryRequirements", "Include Salary Requirements?", false);
      pg.AddCheckBox(mAttachResume, "mAttachResume", "Attach Resume?", true);
      base.Entering();
    }

    public override void OnNext()
    {
      OnSuccess();
    }
    public override void OnFinish()
    {
      OnSuccess();
     }
    private void OnSuccess()
    {
      EmailPackage pkg = (EmailPackage) mPersistent;
      pkg.CompanyName = mCompanyName.Text;
      pkg.ToAddress = mToAddress.Text;
      pkg.Salutation = mSalutation.Text;
      pkg.ContactName = mContactName.Text;
      pkg.WebSite = mWebSite.Text;
      pkg.Notes = mNotes.Text;
      pkg.FromAddress = mFromAddress.Text;
      pkg.SubjectLine = mSubjectLine.Text;
      pkg.ResumeFilename = mResumeFilename.Text;
      pkg.TemplateFilename = mCoverLetterTemplateFilename.Text;
      pkg.IncludeSalaryRequirements = mIncludeSalaryRequirements.Checked;
      pkg.AttachResume = mAttachResume.Checked;
    }
  }
}

WizardButtons.cs

Synopsis
using System;
using System.Windows.Forms;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for WizardButtons.
  /// </summary>
  public class WizardButtons
  {
    private readonly string cPreviousName = "mPrevious";
    private readonly string cNextName = "mNext";
    private readonly string cCancelName = "mCancel";
    private readonly string cFinishName = "mFinish";

    private Form mParent;
    private App mApp;
    
    private Button mPrevious;
    private Button mNext;
    private Button mCancel;
    private Button mFinish;

    public WizardButtons(Form parent, App app)
    {
      mParent = parent;
      mApp = app;
      InitializeComponent(parent);
    }
    public void Set(bool prev, bool next, bool cancel, bool finish)
    {
      mPrevious.Enabled = prev;
      mNext.Enabled = next;
      mCancel.Enabled = cancel;
      mFinish.Enabled = finish;
    }

    public int Height
    {
      get
      {
        return mNext.Height;
      }
    }

    private void InitializeComponent(Form parent)
    {
      int hGap = 4;
      int width = 76;
      int hLocation = hGap;
      int vLocation = 0;
      int tabindex = 0;

      this.mPrevious = new System.Windows.Forms.Button();
      this.mNext = new System.Windows.Forms.Button();
      this.mCancel = new System.Windows.Forms.Button();
      this.mFinish = new System.Windows.Forms.Button();

      // 
      // mPrevious
      // 
      this.mPrevious.Location = new System.Drawing.Point(hLocation, vLocation);
      this.mPrevious.Name = cPreviousName;
      this.mPrevious.TabIndex = tabindex;
      this.mPrevious.Text = "< Previous";
      this.mPrevious.Click += new System.EventHandler(mApp.Button_Click);
      hLocation += width + hGap;
      tabindex++;
      // 
      // mNext
      // 
      this.mNext.Location = new System.Drawing.Point(hLocation, vLocation);
      this.mNext.Name = cNextName;
      this.mNext.TabIndex = tabindex;
      this.mNext.Text = "Next >";
      this.mNext.Click += new System.EventHandler(mApp.Button_Click);
      hLocation += hGap + width;
      tabindex++;
      // 
      // mFinish
      // 
      this.mFinish.Location = new System.Drawing.Point(hLocation, vLocation);
      this.mFinish.Name = cFinishName;
      this.mFinish.TabIndex = tabindex;
      this.mFinish.Text = "Finish";
      this.mFinish.Click += new System.EventHandler(mApp.Button_Click);
      hLocation += hGap + width;
      tabindex++;
      // 
      // mCancel
      // 
      this.mCancel.Location = new System.Drawing.Point(hLocation, vLocation);
      this.mCancel.Name = cCancelName;
      this.mCancel.TabIndex = tabindex;
      this.mCancel.Text = "Cancel";
      this.mCancel.Click += new System.EventHandler(mApp.Button_Click);
      hLocation += hGap + width;
      tabindex++;

      parent.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                    this.mFinish,
                                                                    this.mCancel,
                                                                    this.mPrevious,
                                                                    this.mNext});

    }

    public string Name(Button btn)
    {
      if (IsNext(btn)) return "next";
      if (IsPrevious(btn)) return "previous";
      if (IsCancel(btn)) return "cancel";
      if (IsFinish(btn)) return "finish";
      return "unknown";
    }
    public bool IsNext(Button btn)
    {
      return btn.Name.Equals(cNextName);
    }
    public bool IsPrevious(Button btn)
    {
      return btn.Name.Equals(cPreviousName);
    }
    public bool IsCancel(Button btn)
    {
      return btn.Name.Equals(cCancelName);
    }
    public bool IsFinish(Button btn)
    {
      return btn.Name.Equals(cFinishName);
    }
  }
}

XmlLog.cs

Synopsis
using System;
using System.Text;
using System.IO;
using System.Xml;

namespace resumemastercore
{
  /// <summary>
  /// Summary description for XmlLog.
  /// </summary>
  public class XmlLog
  {
    private string mLogPath;

    public XmlLog()
    {
      mLogPath = CurrentProcess.StartupDirectory + @"\log.txt";
    }

    public void WriteEntry(EmailPackage pkg)
    {
      FileStream fs = new FileStream(mLogPath, FileMode.Append);
      WriteEntry(fs, pkg);
      fs.Close();
    }

    internal void WriteEntry(Stream s, EmailPackage pkg)
    {
      XmlTextWriter writer = new XmlTextWriter(new StreamWriter(s));
      writer.Formatting = Formatting.Indented;

      WriteContents(writer, pkg);
     
      writer.WriteEndElement();
      writer.WriteWhitespace(Environment.NewLine);
      writer.Flush();
    }
    private void WriteContents(XmlTextWriter writer, EmailPackage pkg)
    {
      writer.WriteStartElement("email");
      writer.WriteElementString("date", String.Format("{0}", DateTime.Now));
      writer.WriteElementString("toaddress", pkg.ToAddress);
      writer.WriteElementString("fromaddress", pkg.FromAddress);
      writer.WriteElementString("originalresumefilename", pkg.ResumeFilename);
      writer.WriteElementString("originalcoverlettertemplate", pkg.TemplateFilename);
      writer.WriteElementString("bodyformat", pkg.BodyFormat);
      writer.WriteElementString("contactname", pkg.ContactName);
      writer.WriteElementString("companyname", pkg.CompanyName);
      writer.WriteElementString("website", pkg.WebSite);
      writer.WriteElementString("notes", pkg.Notes);
      writer.WriteElementString("savedcoverletterfilename", pkg.SavedCoverLetterFilename);
      writer.WriteElementString("savedresumefilename", pkg.SavedResumeFilename);
    }
  }
}






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010