using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using ISO8601_Class;
using Messagebox_Class;
using SI_Class;
namespace CryptPad
{
/// <summary> NotePad similar but optionally encrypts file contents
/// </summary>
public partial class CryptPad : Form
{
#region Program data
public const string CRYPTPAD_URL = "http://www.frank-t-clark.com/Professional/Papers/CryptPad/CryptPad.html";
// assume no file saved
public static int errorlevel = 1;
public bool automation = false;
public bool automation_script = false;
public int automation_script_line = 0;
string original_CurrentDirectory;
// default file extension
const string sSafe = ".safe";
const string encryption_prefix = "~!@#";
const string encryption_prefix2 = "~!@#$";
//Hold the file name while open for the save action
private string current_file_name;
// hold the FileStream while locked open for the save/discard
private FileStream file_stream;
// does the file need backup
private bool backup;
//Hold the string/regex for the find replace actions
public Regex find_regex;
public Regex findr_regex;
public string find_string; // Escaped version needed when using regex!
public string replace_string; // ditto but not actually required
public bool checkBoxMatchCase;
// current encryption string
public string encryption_string;
public uint encryption_checksum;
public int random_seed;
/// <summary> basic forced read encodings </summary>
private Encoding[] base_read_encodings =
{
// Binary use ANSI default codepage
Encoding.GetEncoding (0),
// ANSI default codepage
Encoding.GetEncoding (0),
// standard UTF-8 encoding
new UTF8Encoding (),
// LE
new UnicodeEncoding(),
// BE
new UnicodeEncoding(true,false),
};
/// <summary> basic auto read and write encodings </summary>
private Encoding[] base_write_encodings =
{
null, // Binary
// ANSI default codepage with exceptions for writing Unicode
Encoding.GetEncoding (0, EncoderExceptionFallback.ExceptionFallback,
DecoderExceptionFallback.ExceptionFallback),
// standard UTF-8 encoding, false BOM, true exception for reading ANSI
new UTF8Encoding (false,true),
// true BOM
new UTF8Encoding (true,true),
// false big endian byte order, true byte order mark, true exception.
new UnicodeEncoding(false,false,true),
new UnicodeEncoding(false,true,true),
new UnicodeEncoding(true,false,true),
new UnicodeEncoding(true,true,true),
};
private Encoding saved_encoding;
/// <summary> enum to match base_write_encodings </summary>
public enum Encodes
{
Binary,
ANSI,
UTF_8_ASCII,
Unicode_UTF_8,
Unicode,
Unicode_BOM,
Unicode_Big_Endian,
Unicode_Big_Endian_BOM
};
private Encodes saved_encoding_enum;
/// <summary> descriptions to match base_write_encodings </summary>
private string[] encodings =
{
"Binary",
"ANSI",
"UTF-8 (ASCII)",
"Unicode (UTF-8)",
"Unicode",
"Unicode (BOM)",
"Unicode (Big-Endian)",
"Unicode (Big-Endian BOM)",
};
/// <summary> items always used together to remember caret location in a window </summary>
public struct Remember
{
public int startline;
public int startcol;
public int startpoint;
}
/// <summary> hold the remembered position </summary>
Remember remember_start = new Remember ();
// status bar update delayed until after keystroke processed
public Timer timerStatus = new System.Windows.Forms.Timer ();
/// <summary> A Settings shortcut </summary>
Properties.Settings Settings = Properties.Settings.Default;
/// <summary> combobox.items initializations used by find and replace </summary>
public string[] combo_search = { "Insert a search pattern." };
public string[] combo_replacement = { "Insert a replacement pattern." };
public string[] combo_base = {
"\\r\\n Carriage Return, New Line",
"\\t Tab",
"\\\\ Backslash"};
public string[] combo_regex_find = {
". Any character" ,
"* zero or more times",
"+ one or more times",
"? zero or one time",
"^ beginning of line",
"$ end of line",
"\\w Any word character",
"\\W Any non-word character",
"\\s Any white-space character",
"\\S Any non-white-space ",
"\\d any decimal digit",
"\\D any non-decimal digit",
"[] Character group",
"[^] Not Character group",
"[a-z] Character range",
"() Captures subexpression",
};
public string[] combo_regex_replace = { "$1 Substitutes subexpression 1" };
#endregion
#region Program Startup
/// <summary> The main entry point for the application.
/// </summary>
[STAThread]
static int Main (string[] args)
{
Application.Run (new CryptPad ());
return errorlevel;
}
#endregion
#region Form code
public CryptPad ()
{
// Required for Windows Form Designer support
InitializeComponent ();
Font = SystemFonts.MessageBoxFont;
}
private void CryptPad_Load (object sender, EventArgs e)
{
// save for Menu, Tools, Command Line
original_CurrentDirectory = Environment.CurrentDirectory;
// No settings?
if (Settings.WindowPosition.IsEmpty)
Settings.Upgrade ();
Backup = Settings.Backup;
CurrentFont = Settings.CurrentFont;
FontColor = Settings.FontColor;
BackgroundColor = Settings.BackColor;
DisplayBefore = Settings.DisplayBefore;
DisplayEncoding = Settings.DisplayEncoding;
DisplayLines = Settings.DisplayLines;
IsStatusBarVisible = Settings.IsStatusBarVisible;
WordWrap = Settings.WordWrap;
if (Settings.WindowPosition.IsEmpty)
Settings.WindowPosition = Bounds;
else
Bounds = Settings.WindowPosition;
StartPosition = FormStartPosition.Manual;
timerStatus.Tick += new EventHandler (timerStatus_Tick);
}
private void CryptPad_Shown (object sender, EventArgs e)
{
// initialize in case there is no open or there is an open error
NewAll (true);
String[] arguments = Environment.GetCommandLineArgs ();
// Command line arguments?
if (1 == arguments.Length)
return;
// Script or file
if (2 == arguments.Length)
{
if (".CRYPTPAD" == Path.GetExtension (arguments[1]).ToUpper ())
{
if (!File.Exists (arguments[1]))
{
DisplayArguments (arguments, "Missing script file!");
return;
}
Do_Script (arguments[1]);
}
else
{
FileOpen (false, null, arguments[1]);
// prepare for Open and Save As dialogs
Environment.CurrentDirectory = Path.GetDirectoryName (Path.GetFullPath (arguments[1]));
}
return;
}
// Script and file
if (3 == arguments.Length)
{
if (".CRYPTPAD" != Path.GetExtension (arguments[1]).ToUpper ())
{
DisplayArguments (arguments, "Not CryptPad script!");
return;
}
if (!File.Exists (arguments[1]))
{
DisplayArguments (arguments, "Missing script file!");
return;
}
// all references relative to the script file
Environment.CurrentDirectory = Path.GetDirectoryName (Path.GetFullPath (arguments[1]));
if (0 < arguments[2].Length)
if (AutoOpen (arguments[2]))
{
DisplayArguments (arguments, "");
return;
}
Do_Script (arguments[1]);
return;
}
// Other arguments same as a script line
if (ProcessArguments (arguments))
DisplayArguments (arguments, "");
}
private void CryptPad_LocationChanged (object sender, EventArgs e)
{
BoundsChanged ();
}
private void CryptPad_SizeChanged (object sender, EventArgs e)
{
BoundsChanged ();
// word wrap changes current line
UpdateStatusBar ();
}
private void BoundsChanged ()
{
// not final settings yet
if (FormStartPosition.Manual != StartPosition)
return;
// don't save maximized or minimized
if (FormWindowState.Normal != WindowState)
return;
Settings.WindowPosition = Bounds;
Settings.Save ();
}
private void CryptPad_FormClosing (object sender, FormClosingEventArgs e)
{
if (DiscardChanges ())
{
e.Cancel = true;
return;
}
// Discard anything open to speed cleanup
NewAll (true);
}
private void txtBody_MouseDown (object sender, MouseEventArgs e)
{
UpdateStatusBar ();
}
private void txtBody_KeyDown (object sender, KeyEventArgs e)
{
// delay status update until after key is processed
timerStatus.Interval = 1;
timerStatus.Start ();
}
private void timerStatus_Tick (Object myObject, EventArgs myEventArgs)
{
timerStatus.Stop ();
UpdateStatusBar ();
}
#endregion
#region Command Line Automation
/// <summary> Command Line Automation processing
/// </summary>
/// <param name="arguments">arguments to process from command line or script</param>
/// <returns>indicates non-handled failure</returns>
private bool ProcessArguments (String[] arguments)
{
if ("DO" != arguments[1].ToUpper ())
{
DisplayArguments (arguments, "Argument 1 must be 'DO'!");
return false;
}
// There must be at least three arguments
if (3 > arguments.Length)
{
DisplayArguments (arguments, "Not enough arguments!");
return false;
}
string s = arguments[2].ToUpper ();
switch (s)
{
case "SCRIPT":
if (automation_script)
{
DisplayArguments (arguments, "Nested SCRIPT is not allowed!");
return false;
}
// There must be four arguments
if (4 != arguments.Length)
{
DisplayArguments (arguments, "Four arguments required!");
return false;
}
if (!File.Exists (arguments[3]))
{
DisplayArguments (arguments, "Missing script file!");
return false;
}
Do_Script (arguments[3]);
break;
case "ANSI":
automation = true;
if (3 == arguments.Length)
{
if (null == current_file_name)
{
DisplayArguments (arguments, "No open file!");
return false;
}
break;
}
if (4 < arguments.Length)
{
DisplayArguments (arguments, "Too many arguments!");
return false;
}
if (AutoOpen (arguments[3]))
return true;
if (1252 != saved_encoding.CodePage)
{
menuFileForceANSI_Click (null, null);
menuFileSave_Click (null, null);
}
if (!automation_script)
menuFileExit_Click (null, null);
break;
case "UTF-8":
automation = true;
if (3 == arguments.Length)
{
if (null == current_file_name)
{
DisplayArguments (arguments, "No open file!");
return false;
}
break;
}
if (4 < arguments.Length)
{
DisplayArguments (arguments, "Too many arguments!");
return false;
}
if (AutoOpen (arguments[3]))
return true;
if (65001 != saved_encoding.CodePage)
{
menuFileForceASCII_Click (null, null);
menuFileSave_Click (null, null);
}
if (!automation_script)
menuFileExit_Click (null, null);
break;
case "REPLACE":
if (Arg5Test (arguments))
return false;
Replace (Unescape (arguments[3]), Unescape (arguments[4]));
break;
case "REPLACEMATCH":
if (Arg5Test (arguments))
return false;
ReplaceMatch (Unescape (arguments[3]), Unescape (arguments[4]));
break;
case "REGEX":
if (Arg5Test (arguments))
return false;
txtBody.Text = Regex.Replace (txtBody.Text, Unescape (arguments[3]), Unescape (arguments[4]),
RegexOptions.Multiline | RegexOptions.IgnoreCase);
break;
case "REGEXMATCH":
if (Arg5Test (arguments))
return false;
txtBody.Text = Regex.Replace (txtBody.Text, Unescape (arguments[3]), Unescape (arguments[4]),
RegexOptions.Multiline);
break;
case "READONLY":
if (automation_script)
{
DisplayArguments (arguments, "READONLY is only allowed on command line!");
return false;
}
if (4 != arguments.Length)
{
DisplayArguments (arguments, "Four arguments required!");
return false;
}
ReadOnly = true;
if (FileOpen (false, null, arguments[3]))
return true;
// prepare for Open and Save As dialogs
Environment.CurrentDirectory = Path.GetDirectoryName (Path.GetFullPath (arguments[3]));
break;
case "EDIT":
if (4 != arguments.Length)
{
DisplayArguments (arguments, "Four arguments required!");
return false;
}
if (AutoOpen (arguments[3]))
return true;
// prepare for Open and Save As dialogs
Environment.CurrentDirectory = Path.GetDirectoryName (Path.GetFullPath (arguments[3]));
break;
default:
DisplayArguments (arguments, s + " is an unrecognized command!");
break;
}
return false;
}
private bool Arg5Test (String[] arguments)
{
if (!automation_script)
{
DisplayArguments (arguments, "SCRIPT command!");
return true;
}
if (5 != arguments.Length)
{
DisplayArguments (arguments, "Five arguments required!");
return true;
}
if (null == current_file_name)
{
DisplayArguments (arguments, "No open file!");
return true;
}
return false;
}
private void ReplaceMatch (string f, string r)
{
string s = txtBody.Text;
if (0 <= s.IndexOf (f))
{
txtBody.Text = s.Replace (f, r);
txtBody.Modified = true;
}
}
private void Replace (string f, string r)
{
string s = txtBody.Text;
int i = 0;
int jf = f.Length;
int jr = r.Length;
bool c = false;
for (; ; )
{
i = s.IndexOf (f, i, StringComparison.CurrentCultureIgnoreCase);
if (0 > i)
break;
c = true;
s = s.Substring (0, i) + r + s.Substring (i + jf);
i += jr;
}
if (c)
{
txtBody.Text = s;
txtBody.Modified = true;
}
}
/// <summary> Automation open never changes Environment.CurrentDirectory
/// </summary>
/// <param name="path"></param>
private bool AutoOpen (string path)
{
// this should always save
DiscardChanges ();
NewAll (false);
if (FileOpen (false, null, path))
return true;
return false;
}
/// <summary> Display command line or script arguments
/// </summary>
/// <param name="arguments">string array of arguments</param>
/// <param name="s">optional error string</param>
private void DisplayArguments (String[] arguments, string s)
{
StringBuilder st = new StringBuilder ();
if (automation_script)
{
st.AppendLine ("Automation Script Line: " + automation_script_line);
st.AppendLine ();
}
if (null != s)
{
st.AppendLine ("Command Line Automation is exiting immediately!");
st.AppendLine ();
if ("" != s)
{
st.AppendLine (s);
st.AppendLine ();
}
}
st.AppendLine ("Environment.CurrentDirectory = " + original_CurrentDirectory);
st.AppendLine ();
st.AppendLine ("Environment.GetCommandLineArgs.Length = " + arguments.Length);
st.AppendLine ();
int i;
for (i = 0; i < arguments.Length; ++i)
st.AppendLine (String.Format ("{0} = {1}", i, arguments[i]));
Messagebox.Show (st.ToString ());
if (null != s)
{
errorlevel = 2;
menuFileExit_Click (null, null);
}
}
private void Do_Script (string path)
{
automation_script = true;
automation = true;
path = Path.GetFullPath (path);
// all file references in this script are relative to the script
Environment.CurrentDirectory = Path.GetDirectoryName (Path.GetFullPath (path));
StreamReader streamreader = new StreamReader (path);
List<string> arguments = new List<string> ();
automation_script_line = 0;
while (!streamreader.EndOfStream)
{
arguments.Clear ();
arguments.Add (path);
string s = streamreader.ReadLine ();
++automation_script_line;
s = s.Replace ('\0', ' ');
s = s.Replace ("\\\"", "\0");
int istart = 0;
int iend = 0;
string arg;
for (; ; )
{
// skip leading spaces
while (istart < s.Length)
if (' ' == s[istart])
++istart;
else
break;
if (istart == s.Length)
break;
if ('"' == s[istart])
{
++istart;
iend = s.IndexOf ('\"', istart);
if (0 > iend)
{
arguments.Add (s);
DisplayArguments (arguments.ToArray (), "No terminating quote!");
arguments.Clear ();
break;
}
arg = s.Substring (istart, iend - istart);
++iend;
istart = iend;
if (s.Length > iend && ' ' != s[iend])
{
arguments.Add (s);
DisplayArguments (arguments.ToArray (), "Terminating quote not followed by a space!");
arguments.Clear ();
break;
}
}
else
{
iend = s.IndexOf (' ', istart);
if (0 > iend)
iend = s.Length;
arg = s.Substring (istart, iend - istart);
istart = iend;
}
arg = arg.Replace ("\0", "\\\"");
// special case to insert a quote from a command line
arg = arg.Replace ("\\q", "\"");
arguments.Add (arg);
}
if (0 == arguments.Count)
break;
if (1 == arguments.Count)
continue;
if (ProcessArguments (arguments.ToArray ()))
DisplayArguments (arguments.ToArray (), "");
if (2 == errorlevel)
break;
}
streamreader.Close ();
menuFileExit_Click (null, null);
}
#endregion
#region File Menu Items
private void menuFileNew_Click (object sender, System.EventArgs e)
{
if (DiscardChanges ())
return;
NewAll (true);
}
private void menuFileOpen_Click (object sender, System.EventArgs e)
{
if (DiscardChanges ())
return;
NewAll (false);
OpenFileDialog openFileDialog = new OpenFileDialog ();
openFileDialog.InitialDirectory = Environment.CurrentDirectory;
// the first entries must match base_read_encodings array
openFileDialog.Filter =
"Binary (*.*)|*.*|"
+ "ANSI encoding (*.*)|*.*|"
+ "UTF-8 encoding (*.*)|*.*|"
+ "Unicode encoding (*.*)|*.*|"
+ "Unicode (Big-Endian) encoding (*.*)|*.*|"
+ "Open read-only (*.*)|*.*|"
+ "Encrypted documents (*.safe)|*.safe|"
+ "Text Documents (*.txt)|*.txt|"
+ "All documents (*.safe, *.txt)|*.safe;*.txt|"
+ "All Files (*.*)|*.*";
int i = base_read_encodings.Length + 2;
if (null == encryption_string)
++i;
openFileDialog.FilterIndex = i;
// can't use this because it causes an ugly old style dialog box
// openFileDialog.ShowReadOnly = true;
if (DialogResult.Cancel == openFileDialog.ShowDialog (this))
{
NewAll (true);
return;
}
// Did they select read-only
if (openFileDialog.FilterIndex == base_read_encodings.Length + 1)
ReadOnly = true;
else
ReadOnly = false;
// Did they select forced encoding
Encoding force_encoding;
bool binary_encoding = false;
if (1 == openFileDialog.FilterIndex)
binary_encoding = true;
if (openFileDialog.FilterIndex < base_read_encodings.Length + 1)
force_encoding = base_read_encodings[openFileDialog.FilterIndex - 1];
else
force_encoding = null;
Environment.CurrentDirectory = Path.GetDirectoryName (openFileDialog.FileName);
FileOpen (binary_encoding, force_encoding, openFileDialog.FileName);
}
private void menuFileSave_Click (object sender, System.EventArgs e)
{
FileSave ();
}
private void menuFileSaveAs_Click (object sender, System.EventArgs e)
{
FileSaveAs ();
}
private void menuFileReOpenBinary_Click (object sender, EventArgs e)
{
if (DiscardChanges ())
return;
string file_name = current_file_name;
NewAll (false);
FileOpen (true, base_read_encodings[0], file_name);
}
private void menuFileReOpenANSI_Click (object sender, EventArgs e)
{
if (DiscardChanges ())
return;
string file_name = current_file_name;
NewAll (false);
FileOpen (false, base_read_encodings[1], file_name);
}
private void menuFileReOpenUTF8_Click (object sender, EventArgs e)
{
if (DiscardChanges ())
return;
string file_name = current_file_name;
NewAll (false);
FileOpen (false, base_read_encodings[2], file_name);
}
private void menuFileReOpenUnicode_Click (object sender, EventArgs e)
{
if (DiscardChanges ())
return;
string file_name = current_file_name;
NewAll (false);
FileOpen (false, base_read_encodings[3], file_name);
}
private void menuFileReOpenUnicodeBE_Click (object sender, EventArgs e)
{
if (DiscardChanges ())
return;
string file_name = current_file_name;
NewAll (false);
FileOpen (false, base_read_encodings[4], file_name);
}
private void menuFileForceANSI_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.ANSI));
txtBody.Modified = true;
}
private void menuFileForceASCII_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.UTF_8_ASCII));
txtBody.Modified = true;
}
private void menuFileForceUTF8_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.Unicode_UTF_8));
txtBody.Modified = true;
}
private void menuFileForceUnicode_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.Unicode));
txtBody.Modified = true;
}
private void menuFileForceUnicodeBOM_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.Unicode_BOM));
txtBody.Modified = true;
}
private void menuFileForceUnicodeBE_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.Unicode_Big_Endian));
txtBody.Modified = true;
}
private void menuFileForceUnicodeBEBOM_Click (object sender, EventArgs e)
{
UpdateEncoding (Encode (Encodes.Unicode_Big_Endian_BOM));
txtBody.Modified = true;
}
private void menuFileEncryption_Click (object sender, EventArgs e)
{
uint save_checksum = encryption_checksum;
encryption_checksum = 0;
if (DialogResult.Cancel == Encryption ())
encryption_checksum = save_checksum;
else
txtBody.Modified = true;
}
private void menuFileExit_Click (object sender, EventArgs e)
{
if (DiscardChanges ())
return;
Application.Exit ();
}
#endregion
#region Edit Menu Items
private void menuEdit_Popup (object sender, EventArgs e)
{
menuEditUndo.Enabled = !ReadOnly & txtBody.CanUndo;
menuEditPaste.Enabled = !ReadOnly & Clipboard.ContainsText ();
bool f = (0 < txtBody.SelectionLength);
menuEditCut.Enabled = !ReadOnly & f;
menuEditCopy.Enabled = f;
menuEditDelete.Enabled = !ReadOnly & f;
menuEditCapitalize.Enabled = !ReadOnly & f;
menuEditLowercase.Enabled = !ReadOnly & f;
menuEditUppercase.Enabled = !ReadOnly & f;
menuEditTranspose.Enabled = !ReadOnly & !f;
}
private void menuEditUndo_Click (object sender, EventArgs e)
{
txtBody.Undo ();
}
private void menuEditCut_Click (object sender, System.EventArgs e)
{
txtBody.Cut ();
}
private void menuEditCopy_Click (object sender, System.EventArgs e)
{
txtBody.Copy ();
}
private void menuEditPaste_Click (object sender, System.EventArgs e)
{
txtBody.Paste ();
}
private void menuEditDelete_Click (object sender, EventArgs e)
{
txtBody.Paste ("");
}
private void menuEditUppercase_Click (object sender, EventArgs e)
{
if (0 == txtBody.SelectionLength)
return;
int s = txtBody.SelectionStart;
int l = txtBody.SelectionLength;
txtBody.Paste (txtBody.SelectedText.ToUpper ());
txtBody.Select (s, l);
}
private void menuEditLowercase_Click (object sender, EventArgs e)
{
if (0 == txtBody.SelectionLength)
return;
int s = txtBody.SelectionStart;
int l = txtBody.SelectionLength;
txtBody.Paste (txtBody.SelectedText.ToLower ());
txtBody.Select (s, l);
}
private void menuEditCapitalize_Click (object sender, EventArgs e)
{
int i;
if (0 == txtBody.SelectionLength)
return;
String s = " " + txtBody.SelectedText.ToLower ();
i = 0;
for (; ; )
{
i = s.IndexOf (' ', i);
if (0 > i || i == s.Length - 1)
break;
++i;
s = s.Substring (0, i) + s.Substring (i, 1).ToUpper () + s.Substring (i + 1);
}
i = txtBody.SelectionStart;
int l = txtBody.SelectionLength;
txtBody.Paste (s.Substring (1));
txtBody.Select (i, l);
}
private void menuEditTranspose_Click (object sender, EventArgs e)
{
if (0 < txtBody.SelectionLength)
return;
int i = txtBody.SelectionStart;
if (0 == i || txtBody.Text.Length == i)
return;
txtBody.Select (i - 1, 2);
String s = txtBody.SelectedText;
if (!isASCII (s[0]) || !isASCII (s[1]))
return;
s = s[1].ToString () + s[0].ToString ();
txtBody.Paste (s);
txtBody.SelectionStart = i;
}
public Form find_Form; // non-modal return here
private void menuEditFind_Click (object sender, EventArgs e)
{
if (txtBody.Text.Length == 0)
return;
if (null != replace_Form)
replace_Form.Close ();
if (null == find_Form)
{
find_Form = new Find ();
find_Form.Show (this);
}
else
find_Form.Focus ();
}
private void menuEditFindNext_Click (object sender, EventArgs e)
{
FindNext (true);
}
private void menuEditFindPrevious_Click (object sender, EventArgs e)
{
FindNext (false);
}
public Form replace_Form; // non-modal return here
private void menuEditReplace_Click (object sender, EventArgs e)
{
if (txtBody.Text.Length == 0)
return;
if (null != find_Form)
find_Form.Close ();
if (null == replace_Form)
{
replace_Form = new Replace ();
replace_Form.Show (this);
}
else
replace_Form.Focus ();
}
private void menuEditGoTo_Click (object sender, EventArgs e)
{
GoTo form = new GoTo ();
form.ShowDialog (this);
UpdateStatusBar ();
}
private void menuEditSelectAll_Click (object sender, System.EventArgs e)
{
txtBody.SelectAll ();
}
private void menuEditTimeDate_Click (object sender, EventArgs e)
{
txtBody.Paste (ISO8601.DateTimeHM ());
}
#endregion
#region Tools Menu Items
private void menuToolsPassword_Click (object sender, EventArgs e)
{
string s = "";
Random r = new Random ();
for (int i = 0; i < 16; ++i)
{
int j = r.Next (62);
if (j < 10)
s += (char) ('0' + j);
else if (j < 36)
s += (char) ('A' + j - 10);
else
s += (char) ('a' + j - 36);
}
txtBody.Paste (s);
}
private void menuToolsQuickCrypt_Click (object sender, EventArgs e)
{
string s;
Cursor.Current = Cursors.WaitCursor;
if (txtBody.Text.StartsWith (encryption_prefix)
|| txtBody.Text.StartsWith (encryption_prefix2))
s = Decrypt (txtBody.Text);
else
s = Encrypt (txtBody.Text);
if (null == s)
return;
txtBody.SelectAll ();
txtBody.Paste (s);
txtBody.SelectionStart = 0; // keep text from being selected
}
private void menuToolsASCII_Click (object sender, EventArgs e)
{
int i, j;
Cursor.Current = Cursors.WaitCursor;
string s = txtBody.Text;
j = txtBody.Text.Length;
for (i = txtBody.SelectionStart + txtBody.SelectionLength; i < j; ++i)
{
if (127 < s[i])
break;
}
if (i == j)
{
Messagebox.Show ("No non-ASCII characters found!");
return;
}
SelectScroll (i, 1);
}
private void menuToolsLong_Click (object sender, EventArgs e)
{
int i, j, l;
Cursor.Current = Cursors.WaitCursor;
string[] s = txtBody.Lines;
j = s.Length;
int length = 0;
int index = -1;
int select = 0;
for (i = 0; i < j; ++i)
{
l = s[i].Length;
select += l + 2;
if (length < l)
{
length = l;
index = select;
}
}
SelectScroll (index - 2, 0);
}
private void menuToolsBlank_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
int i = Ctrl (txtBody.Text, txtBody.SelectionStart);
if (0 > i)
{
RestoreCaretPosition ();
Messagebox.Show ("No ANSI Blank found!");
return;
}
txtBody.Select (i, 1);
int j = txtBody.Text[i];
string s = "\\x" + j.ToString ("X2");
txtBody.Paste (s);
SelectScroll (i, 4);
}
private void menuToolsCR_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
string s = txtBody.Text;
s = s.Replace ("\r\n", "\n");
s = s.Replace ("\r", "\n");
s = s.Replace ("\n", "\r\n");
txtBody.SelectAll ();
txtBody.Paste (s);
RestoreCaretPosition ();
}
private void menuToolsLF_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
string s = txtBody.Text;
s = s.Replace ("\r\n", "\n");
s = s.Replace ("\r", "\n");
if ('\n' != s[s.Length - 1])
s += "\n";
txtBody.SelectAll ();
txtBody.Paste (s);
RestoreCaretPosition ();
}
private void menuToolsDangle_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
string s = txtBody.Text;
int i = CRLF (s);
if (0 > i)
{
RestoreCaretPosition ();
Messagebox.Show ("No Dangling CR or LF found!");
return;
}
txtBody.Select (i, 1);
if ('\r' == s[i])
s = "\\r";
else
s = "\\n";
txtBody.Paste (s);
SelectScroll (i, 2);
}
private void menuToolsWrap_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
string s = txtBody.Text;
for (int line = txtBody.GetLineFromCharIndex (txtBody.TextLength); line > 0; --line)
{
int i = txtBody.GetFirstCharIndexFromLine (line);
if (' ' == s[i - 1])
s = s.Insert (i, "\r\n");
}
txtBody.SelectAll ();
txtBody.Paste (s);
RestoreCaretPosition ();
}
private void menuToolsUnwrap_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
string s = txtBody.Text;
s = s.Replace (" \r\n", " ");
txtBody.SelectAll ();
txtBody.Paste (s);
RestoreCaretPosition ();
}
private void menuToolsTrim_Click (object sender, EventArgs e)
{
RememberCaretPosition ();
string s = txtBody.Text;
s = s.Replace (" \r\n", "\r\n");
s = s.Replace (" \r\n", "\r\n");
s = s.Replace ("\r\n ", "\r\n");
s = s.Replace ("\r\n ", "\r\n");
txtBody.SelectAll ();
txtBody.Paste (s);
RestoreCaretPosition ();
}
private void menuToolsCommandLine_Click (object sender, EventArgs e)
{
DisplayArguments (Environment.GetCommandLineArgs (), null);
}
private void menuToolsBackup0_Click (object sender, EventArgs e)
{
Backup = 0;
}
private void menuToolsBackup1_Click (object sender, EventArgs e)
{
Backup = 1;
}
private void menuToolsBackup2_Click (object sender, EventArgs e)
{
Backup = 2;
}
private void menuToolsBackup3_Click (object sender, EventArgs e)
{
Backup = 3;
}
#endregion
#region Format Menu Items
private void menuFormatWordWrap_Click (object sender, EventArgs e)
{
int i = txtBody.SelectionStart;
WordWrap = !(menuFormatWordWrap.Checked);
txtBody.DeselectAll ();
SelectScroll (i, 0);
}
private void menuFormatFont_Click (object sender, System.EventArgs e)
{
FontDialog dlgFont = new FontDialog ();
dlgFont.Font = CurrentFont;
dlgFont.FontMustExist = true;
dlgFont.ShowEffects = false;
dlgFont.AllowScriptChange = false;
dlgFont.AllowSimulations = false;
dlgFont.AllowVectorFonts = false;
dlgFont.AllowVerticalFonts = false;
if (DialogResult.Cancel == dlgFont.ShowDialog (this))
return;
CurrentFont = dlgFont.Font;
}
private void menuFormatFontColor_Click (object sender, EventArgs e)
{
ColorDialog dlgColor = new ColorDialog ();
dlgColor.Color = FontColor;
dlgColor.AnyColor = true;
if (DialogResult.Cancel == dlgColor.ShowDialog (this))
return;
FontColor = dlgColor.Color;
}
private void menuFormatColor_Click (object sender, EventArgs e)
{
ColorDialog dlgColor = new ColorDialog ();
dlgColor.Color = BackgroundColor;
dlgColor.AnyColor = true;
if (DialogResult.Cancel == dlgColor.ShowDialog (this))
return;
BackgroundColor = dlgColor.Color;
}
#endregion
#region View Menu Items
private void menuViewStatus_Click (object sender, EventArgs e)
{
IsStatusBarVisible = !menuViewStatus.Checked;
UpdateStatusBar ();
}
private void menuViewBefore_Click (object sender, EventArgs e)
{
DisplayBefore = !menuViewBefore.Checked;
UpdateStatusBar ();
}
private void menuViewLines_Click (object sender, EventArgs e)
{
DisplayLines = !menuViewLines.Checked;
UpdateStatusBar ();
}
private void menuViewEncoding_Click (object sender, EventArgs e)
{
DisplayEncoding = !menuViewEncoding.Checked;
}
#endregion
#region Help Menu Items
private void menuHelpOnline_Click (object sender, EventArgs e)
{
Process process = new Process ();
process.StartInfo.FileName = CRYPTPAD_URL;
bool result = process.Start ();
}
private void menuHelpLocal_Click (object sender, EventArgs e)
{
string path = AppDomain.CurrentDomain.BaseDirectory + "\\CryptPad.html";
if (!File.Exists (path))
{
string[] lines = { "Open online location ", CRYPTPAD_URL, " and save a local copy to ", path };
System.IO.File.WriteAllLines (path, lines);
}
Process process = new Process ();
process.StartInfo.FileName = path;
bool result = process.Start ();
}
private void menuHelpAbout_Click (object sender, EventArgs e)
{
About form = new About ();
form.ShowDialog (this);
}
#endregion
#region Settings code
private int Backup
{
get
{
return Settings.Backup;
}
set
{
Settings.Backup = value;
menuToolsBackup0.Checked = false;
menuToolsBackup1.Checked = false;
menuToolsBackup2.Checked = false;
menuToolsBackup3.Checked = false;
switch (value)
{
default:
case 0:
menuToolsBackup0.Checked = true;
break;
case 1:
menuToolsBackup1.Checked = true;
break;
case 2:
menuToolsBackup2.Checked = true;
break;
case 3:
menuToolsBackup3.Checked = true;
break;
}
Settings.Save ();
}
}
private Font CurrentFont
{
get
{
return Settings.CurrentFont;
}
set
{
txtBody.Font = Settings.CurrentFont = value;
Settings.Save ();
}
}
private Color FontColor
{
get
{
return Settings.FontColor;
}
set
{
txtBody.ForeColor = Settings.FontColor = value;
Settings.Save ();
}
}
private Color BackgroundColor
{
get
{
return Settings.BackColor;
}
set
{
txtBody.BackColor = Settings.BackColor = value;
Settings.Save ();
}
}
public bool DisplayBefore
{
get
{
return Settings.DisplayBefore;
}
set
{
menuViewBefore.Checked = Settings.DisplayBefore = value;
Settings.Save ();
}
}
public bool DisplayEncoding
{
get
{
return Settings.DisplayEncoding;
}
set
{
menuViewEncoding.Checked = Settings.DisplayEncoding = value;
Settings.Save ();
if (menuViewEncoding.Checked)
{
statusStrip.Visible = true;
statusStripLabel4.Visible = true;
}
else
statusStripLabel4.Visible = false;
}
}
public bool DisplayLines
{
get
{
return Settings.DisplayLines;
}
set
{
menuViewLines.Checked = Settings.DisplayLines = value;
Settings.Save ();
}
}
/// <summary> Display Line/Char
/// </summary>
public bool IsStatusBarVisible
{
get
{
return Settings.IsStatusBarVisible;
}
set
{
menuViewStatus.Checked = Settings.IsStatusBarVisible = value;
Settings.Save ();
}
}
public bool WordWrap
{
get
{
return Settings.WordWrap;
}
set
{
menuFormatWordWrap.Checked = txtBody.WordWrap = Settings.WordWrap = value;
Settings.Save ();
}
}
#endregion
#region Shared functions
/// <summary> Set original blank file
/// </summary>
/// <param name="clear_encrypt">Clear any previous encryption string</param>
private void NewAll (bool clear_encrypt)
{
if (clear_encrypt)
{
encryption_string = null;
encryption_checksum = 0;
}
if (null != file_stream)
{
file_stream.Close ();
file_stream = null;
}
ReadOnly = false;
UpdateEncoding (Encode (Encodes.UTF_8_ASCII));
txtBody.Clear ();
txtBody.SelectionStart = 0; // keep text from being selected
Title (null);
}
private bool FileOpen (bool binary_encoding, Encoding use_encoding, string original_filename)
{
int i;
string s;
string filename = Path.GetFullPath (original_filename);
if (!File.Exists (filename))
{
Messagebox.Show ("File does not exist!\r\n\r\n" + filename);
return true;
}
// refuse to open a file with a backup extension of 1, 2, or 3.
s = Path.GetExtension (filename);
if (s == ".1" || s == ".2" || s == ".3")
{
Messagebox.Show ("Cannot open a backup file!\r\n\r\n" + filename);
return true;
}
if (!ReadOnly)
{
s = null;
try
{
file_stream = new FileStream (filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
backup = true;
}
catch (Exception err)
{
s = err.Message;
}
if (null != s)
{
// we don't do read-only in automation
if (automation)
return true;
s += " Open read-only?";
DialogResult code = Warning_Popup (s);
if (DialogResult.Cancel == code)
return true;
ReadOnly = true;
}
}
if (ReadOnly)
{
s = null;
try
{
file_stream = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
catch (Exception err)
{
s = err.Message;
}
if (null != s)
{
Messagebox.Show (s);
return true;
}
}
if (file_stream.Length > SI.ParseI32 ("2M"))
{
s = "File size is about " + SI.ToString (file_stream.Length)
+ "! While it may possible to edit a file of this size, it will be slow.";
if (DialogResult.Cancel == Warning_Popup (s))
{
NewAll (false);
return true;
}
}
StreamReader sr;
// is it forced encoding?
if (null == use_encoding)
// automatic attempt UTF-8 encoding with ANSI error exception, BOM detect
sr = new StreamReader (file_stream, Encode (Encodes.UTF_8_ASCII), true);
else
sr = new StreamReader (file_stream, use_encoding, false);
Cursor.Current = Cursors.WaitCursor;
s = null;
try
{
// fast part for big files
s = sr.ReadToEnd ();
UpdateEncoding (sr.CurrentEncoding);
}
catch
{
// ANSI error detection
s = null;
}
if (s == null && file_stream.Position > 0)
{
// use ANSI
UpdateEncoding (Encode (Encodes.ANSI));
file_stream.Position = 0;
sr = new StreamReader (file_stream, saved_encoding);
s = sr.ReadToEnd ();
}
if (ReadOnly)
sr.Close ();
else
// don't close or dispose because it closes the underlying file_stream
sr.DiscardBufferedData ();
// binary data from file
byte[] file_bytes = null;
// did they force binary?
if (binary_encoding)
{
file_bytes = saved_encoding.GetBytes (s);
UpdateEncoding (Encode (Encodes.Binary));
}
// is the encoding automatic and ambiguous and contains NUL?
else if ((null == use_encoding) && (0 == saved_encoding.GetPreamble ().Length) && (0 <= s.IndexOf ("\x0")))
{
// ANSI and UTF-8 ASCII do not change bytes so no need to reread
file_bytes = saved_encoding.GetBytes (s);
// we are interested in the last two bytes
i = file_bytes.Length - 2;
// tentative assume binary for ANSI
if (Encodes.ANSI == saved_encoding_enum)
saved_encoding_enum = Encodes.Binary;
// Unicode requires an even nmber of bytes
if (0 == (i & 1))
{
if ('\n' == file_bytes[i] && '\x0' == file_bytes[i + 1])
{
if (TestEncode (Encode (Encodes.Unicode), file_bytes))
s = base_read_encodings[3].GetString (file_bytes);
}
else if ('\x0' == file_bytes[i] && '\n' == file_bytes[i + 1])
{
if (TestEncode (Encode (Encodes.Unicode_Big_Endian), file_bytes))
s = base_read_encodings[4].GetString (file_bytes);
}
}
// final update if determined binary
if (Encodes.Binary == saved_encoding_enum)
UpdateEncoding (Encode (Encodes.Binary));
}
// only warn for non-binary
if (null != saved_encoding)
{
// discard binary data
file_bytes = null;
i = s.IndexOf ("\x0");
if (0 <= i)
{
string str2 = "NUL (zero value) character at index " + i + " found!"
+ " Select \"OK\" to change all NUL to a space or \"Cancel\" to abort file open.";
if (DialogResult.Cancel == Warning_Popup (str2))
{
NewAll (false);
return true;
}
s = s.Replace ("\x0", " ");
}
i = CRLF (s);
if (0 <= i)
{
string str2 = "A dangling CR or LF character at index " + i + " found! "
+ "Select \"OK\" to Force CR/LF or \"Cancel\" to ignore.";
if (DialogResult.Cancel != Warning_Popup (str2))
{
s = s.Replace ("\r", "");
s = s.Replace ("\n", "\r\n");
}
}
i = Ctrl (s, 0);
if (0 <= i)
Messagebox.Show ("ANSI Blank character at index " + i + " found!");
s = Decrypt (s);
}
else
{
// discard encode
s = null;
Messagebox.Show ("Binary file displayed in hexadecimal and ASCII!");
int size = file_bytes.Length;
StringBuilder t = new StringBuilder (size * 5);
for (int b = 0; b < size; b += 16)
{
for (i = 0; i < 16; ++i)
{
if (b + i < size)
{
t.AppendFormat ("{0:X2}", (int) file_bytes[b + i]);
if (15 > i && 3 == i % 4)
t.Append ('·');
else
t.Append (" ");
}
else
t.Append ("\x20\x20\x20");
}
for (i = 0; i < 16; ++i)
if (b + i < size)
{
if ((file_bytes[b + i] >= 32) & (file_bytes[b + i] < 128))
t.Append ((char) file_bytes[b + i]);
else
t.Append ('·');
}
else
t.Append (" ");
t.AppendLine ();
}
s = t.ToString ();
}
// slowest part for big files
txtBody.Text = s;
txtBody.SelectionStart = 0; // keep text from being selected
Title (filename);
return false;
}
/// <summary> Test good encoding or bad accepted
/// </summary>
/// <param name="encoding"></param>
/// <param name="t"></param>
/// <returns></returns>
bool TestEncode (Encoding encoding, byte[] t)
{
string s = null;
try
{
encoding.GetString (t);
UpdateEncoding (encoding);
return true;
}
catch (Exception exception)
{
s = exception.Message;
}
if (DialogResult.OK != Warning_Popup (s))
return false;
UpdateEncoding (encoding);
return true;
}
/// <summary> Return an encoding matching the enumeration
/// </summary>
/// <param name="encode"></param>
/// <returns></returns>
public Encoding Encode (Encodes encode)
{
return base_write_encodings[(int) encode];
}
private void Title (string filename)
{
current_file_name = filename;
// can't ReOpen what ain't a file name
menuFileReOpen.Enabled = null != filename;
Text = (null == current_file_name ? "Untitled" : Path.GetFileName (filename))
+ " - " + Name
+ (ReadOnly ? " (read-only)" : "")
+ (0 < encryption_checksum ? " (Encrypted)" : "");
UpdateStatusBar ();
}
/// <summary> Decrypt if encrypted and ask for encryption phrase, as needed
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private string Decrypt (string s)
{
bool v2;
if (s.StartsWith (encryption_prefix2))
v2 = true;
else if (s.StartsWith (encryption_prefix))
v2 = false;
else
{
encryption_string = null;
encryption_checksum = 0;
return s;
}
StringBuilder st;
int j;
// distinguish version 1 and version 2
if (v2)
{
uint data_checksum = 0;
if (Checksum (s.Substring (5, 8), out data_checksum))
{
encryption_string = null;
encryption_checksum = 0;
return s;
}
j = s.Length;
uint checksum = 0;
for (int i = 13; i < j; ++i)
{
// checksum
uint t = 0;
if (0 < (0x80000000 & checksum))
t = 1;
checksum <<= 1;
checksum |= t;
checksum ^= (uint) s[i];
}
if (data_checksum != checksum)
{
Messagebox.Show ("Encrypted data corruption found!");
encryption_string = null;
encryption_checksum = 0;
return s;
}
st = new StringBuilder (s.Substring (21));
}
else
st = new StringBuilder (s.Substring (12));
if (Checksum (s.Substring (v2 ? 13 : 4, 8), out encryption_checksum))
{
encryption_string = null;
encryption_checksum = 0;
return s;
}
// it will return immediately, if we already have encryption_string
Encryption ();
if (null == encryption_string)
return s;
j = st.Length;
for (int i = 0; i < j; ++i)
{
int chr = st[i];
// decrypt
if (isASCII ((char) chr))
{
chr = ' ' + ((chr - ' ') - E (i) + 95) % 95;
st[i] = (char) chr;
}
}
return st.ToString ();
}
private bool Checksum (string s, out uint checksum)
{
string str2;
// not an encryption_checksum
if (!UInt32.TryParse (s, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out checksum))
return true;
str2 = checksum.ToString ("X8");
// strings don't match!
if (s != str2)
return true;
return false;
}
private bool FileSaveAs ()
{
bool error_flag = false;
SaveFileDialog saveFileDialog = new SaveFileDialog ();
saveFileDialog.FileName = current_file_name;
saveFileDialog.InitialDirectory = Environment.CurrentDirectory;
// the first entries must match base_write_encodings array
saveFileDialog.Filter =
"ANSI encoding (*.*)|*.*|"
+ "UTF-8 (ASCII) encoding (*.*)|*.*|"
+ "Unicode (UTF-8) encoding (*.*)|*.*|"
+ "Unicode encoding (*.*)|*.*|"
+ "Unicode (BOM) encoding (*.*)|*.*|"
+ "Unicode (Big-Endian) encoding (*.*)|*.*|"
+ "Unicode (Big-Endian BOM) encoding (*.*)|*.*|"
+ "Encrypted documents (*.safe)|*.safe|"
+ "Text Documents (*.txt)|*.txt|"
+ "All documents (*.safe, *.txt)|*.safe;*.txt|"
+ "All Files (*.*)|*.*";
int i = base_write_encodings.Length;
if (null == encryption_string)
++i;
saveFileDialog.FilterIndex = i;
if (DialogResult.Cancel == saveFileDialog.ShowDialog ())
return true;
if (base_write_encodings.Length >= saveFileDialog.FilterIndex)
UpdateEncoding (base_write_encodings[saveFileDialog.FilterIndex]);
if (null != file_stream)
file_stream.Close ();
Environment.CurrentDirectory = Path.GetDirectoryName (saveFileDialog.FileName);
try
{
file_stream = new FileStream (saveFileDialog.FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
}
catch (Exception err)
{
Messagebox.Show (err.Message);
error_flag = true;
}
if (error_flag)
return true;
ReadOnly = false;
Title (saveFileDialog.FileName);
backup = true; // we could be overwriting!
bool result = FileSave ();
backup = true;
return result;
}
private bool FileSave ()
{
if (null == current_file_name)
return FileSaveAs ();
Cursor.Current = Cursors.WaitCursor;
string s = txtBody.Text;
if (0<s.Length&&'\n' != s[s.Length - 1])
s += "\r\n";
if (null != encryption_string)
s = Encrypt (s);
if (null == saved_encoding)
saved_encoding = Encode (Encodes.ANSI);
// will unicode save as possible ANSI encoding?
string err_str = null;
byte[] test = null;
try
{
test = saved_encoding.GetBytes (s);
}
catch (Exception err)
{
err_str = err.Message;
}
if (null != err_str)
{
if (Encodes.ANSI != saved_encoding_enum)
{
Messagebox.Show (err_str + "\nUnexpected Encoding Error!");
return true;
}
if (!automation)
{
if (DialogResult.Cancel == Warning_Popup (err_str + "\n\nThis file appears to contain"
+ " characters in Unicode format which may be lost if you save this file as an"
+ " ANSI encoded text file. To keep the Unicode information, click Cancel below and"
+ " then select one of the Unicode options under \"File, Force Encoding\" or from the \"File, SaveAs, Save as Type\" dropdown list."
+ "\r\n\r\nContinue anyway?"))
return true;
}
// Local codepage no exceptions
UpdateEncoding (Encoding.Default);
Cursor.Current = Cursors.WaitCursor;
err_str = null;
try
{
test = saved_encoding.GetBytes (s);
}
catch (Exception err)
{
err_str = err.Message;
}
if (null != err_str)
{
Messagebox.Show (err_str + "\nUnexpected Error!");
return true;
}
// change the screen contents also
s = saved_encoding.GetString (test);
txtBody.Text = s;
txtBody.SelectionStart = 0;
}
// is this the first save after open and backup generation selected
if (backup && 0 < Backup)
{
backup = false;
string filename = file_stream.Name;
for (int i = Backup; i > 1; --i)
Rename (filename + "." + (i - 1), filename + "." + i);
file_stream.Close ();
Rename (filename, filename + ".1");
file_stream = new FileStream (filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
}
else
{
// I am having a problem with losing network connection but it is
// a network or firewall or webserver problem.
file_stream.Seek (0, SeekOrigin.Begin);
// discard any previous possible contents
file_stream.SetLength (0);
}
// use the StreamWriter to automatically handle the optional BOM
StreamWriter sw = new StreamWriter (file_stream, saved_encoding);
sw.Write (s);
sw.Flush ();
txtBody.Modified = false;
errorlevel = 0; // a successful save
return false;
}
/// <summary> Rename backup files with error handling
/// </summary>
/// <param name="source">file path</param>
/// <param name="destination">file path</param>
private void Rename (string source, string destination)
{
if (!File.Exists (source))
return;
if (File.Exists (destination))
{
try
{
File.Delete (destination);
}
catch (Exception e)
{
Messagebox.Show ("Error replacing " + destination + "\r\n\r\n" + e.Message);
return;
}
}
try
{
File.Move (source, destination);
}
catch (Exception e)
{
Messagebox.Show ("Error moving " + source + "\r\n\r\n" + e.Message);
return;
}
}
private string Encrypt (string s)
{
if (null == encryption_string)
if (DialogResult.Cancel == Encryption ())
return s;
StringBuilder st = new StringBuilder (s);
int j = st.Length;
for (int i = 0; i < j; ++i)
{
int chr = st[i];
// encrypt
if (isASCII ((char) chr))
{
chr = ' ' + (((chr - ' ') + E (i)) % 95);
st[i] = (char) chr;
}
}
s = encryption_checksum.ToString ("X8") + st;
j = s.Length;
uint checksum = 0;
for (int i = 0; i < j; ++i)
{
// checksum
uint t = 0;
if (0 < (0x80000000 & checksum))
t = 1;
checksum <<= 1;
checksum |= t;
checksum ^= (uint) s[i];
}
return encryption_prefix2 + checksum.ToString ("X8") + s;
}
/// <summary> Calculate psuedo-random encryption value by position
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private int E (int i)
{
return (encryption_string[i % encryption_string.Length] + i + i * random_seed) % 95;
}
private DialogResult Encryption ()
{
Encryption form = new Encryption ();
DialogResult t = form.ShowDialog (this);
return t;
}
private bool DiscardChanges ()
{
DialogResult result;
if (ReadOnly)
{
txtBody.Modified = false;
return false;
}
if (txtBody.Modified)
{
if (automation)
result = DialogResult.Yes;
else
result = Messagebox.Show (
"Save " + current_file_name + "?",
null,
MessageBoxButtons.YesNoCancel);
switch (result)
{
case DialogResult.Yes:
if (FileSave ())
return true;
break;
case DialogResult.No:
break;
case DialogResult.Cancel:
return true;
}
txtBody.Modified = false;
}
return false;
}
/// <summary> Change Status Bar to reflect changes in txtBody
/// </summary>
private void UpdateStatusBar ()
{
// is it more efficient to change statusStrip.Visible only once?
bool visible = false;
if (DisplayEncoding)
visible = true;
int caret = txtBody.SelectionStart;
if (IsStatusBarVisible)
{
visible = true;
statusStripLabel1.Visible = true;
int line = txtBody.GetLineFromCharIndex (caret);
int col = caret - txtBody.GetFirstCharIndexFromLine (line);
++line;
++col;
statusStripLabel1.Text = "Line: " + line.ToString ("N0")
+ " Char: " + col.ToString ("N0");
}
else
statusStripLabel1.Visible = false;
if (DisplayBefore)
{
visible = true;
statusStripLabel2.Visible = true;
statusStripLabel2.Text = "Before: " + caret.ToString ("N0")
+ " After: " + (txtBody.TextLength - caret).ToString ("N0");
}
else
statusStripLabel2.Visible = false;
if (DisplayLines)
{
visible = true;
statusStripLabel3.Visible = true;
statusStripLabel3.Text = "Lines: " + txtBody.Lines.Length.ToString ("N0")
+ " Chars: " + txtBody.TextLength.ToString ("N0");
}
else
statusStripLabel3.Visible = false;
statusStrip.Visible = visible;
}
/// <summary> changes rarely and would add to processing
/// </summary>
private void UpdateEncoding (Encoding new_encoding)
{
#if false // values display for debugging
for (int i = 0; i < encodings.Length; ++i)
{
Encoding t = base_encodings[i];
Console.WriteLine (encodings[i] + "\tEncodingName: " + t.EncodingName + "\tHeaderName: " + t.HeaderName + "\tCodePage: " + t.CodePage + "\tBOM: " + t.GetPreamble ().Length);
}
#endif
// assume all are ok to start
menuFileReOpenBinary.Enabled = true;
menuFileReOpenANSI.Enabled = true;
menuFileReOpenUTF8.Enabled = true;
menuFileReOpenUnicode.Enabled = true;
menuFileReOpenUnicodeBE.Enabled = true;
saved_encoding = new_encoding;
if (null == saved_encoding)
{
menuFileReOpenBinary.Enabled = false;
saved_encoding_enum = Encodes.Binary;
ReadOnly = true;
}
else
{
switch (saved_encoding.CodePage)
{
case 65001:
menuFileReOpenUTF8.Enabled = false;
saved_encoding_enum = Encodes.UTF_8_ASCII;
break;
case 1200:
menuFileReOpenUnicode.Enabled = false;
saved_encoding_enum = Encodes.Unicode;
break;
case 1201:
menuFileReOpenUnicodeBE.Enabled = false;
saved_encoding_enum = Encodes.Unicode_Big_Endian;
break;
default:
menuFileReOpenANSI.Enabled = false;
saved_encoding_enum = Encodes.ANSI;
break;
}
if (0 < saved_encoding.GetPreamble ().Length)
saved_encoding_enum++;
}
menuFileForceANSI.Enabled = Encodes.ANSI != saved_encoding_enum;
menuFileForceASCII.Enabled = Encodes.UTF_8_ASCII != saved_encoding_enum;
menuFileForceUTF8.Enabled = Encodes.Unicode_UTF_8 != saved_encoding_enum;
menuFileForceUnicode.Enabled = Encodes.Unicode != saved_encoding_enum;
menuFileForceUnicodeBOM.Enabled = Encodes.Unicode_BOM != saved_encoding_enum;
menuFileForceUnicodeBE.Enabled = Encodes.Unicode_Big_Endian != saved_encoding_enum;
menuFileForceUnicodeBEBOM.Enabled = Encodes.Unicode_Big_Endian_BOM != saved_encoding_enum;
if (Encodes.ANSI == saved_encoding_enum)
statusStripLabel4.Text = saved_encoding.HeaderName;
else
statusStripLabel4.Text = encodings[(int) saved_encoding_enum];
}
public DialogResult Warning_Popup (string s)
{
return Messagebox.Show (s, null, MessageBoxButtons.OKCancel);
}
/// <summary> Find and Replace non-modal dialogs need special positioning
/// </summary>
/// <param name="dialog"></param>
public void Position (Form dialog)
{
// center horizontally
dialog.Left = Left + (Width - dialog.Width) / 2;
int t = Screen.GetWorkingArea (this).Right;
if (0 > dialog.Left)
dialog.Left = 0;
else if (t < dialog.Right)
dialog.Left -= dialog.Right - t;
// Center vertically dodging the caret
dialog.Top = Top + (Height - dialog.Height) / 2;
t = txtBody.PointToScreen (txtBody.GetPositionFromCharIndex (txtBody.SelectionStart)).Y;
int h = (int) txtBody.Font.Height * 3;
// is the selection area covered by the dialog
if (t + h > dialog.Top && t - h < dialog.Bottom)
// is it closer to move the dialog down
if (t - h < dialog.Bottom - dialog.Height)
dialog.Top = t + h;
else
dialog.Top = t - h - dialog.Height;
}
/// <summary> Find string
/// </summary>
/// <param name="next">next or false means previous</param>
public void FindNext (bool next)
{
Match match;
if (null == find_string)
{
menuEditFind_Click (null, null);
return;
}
Cursor.Current = Cursors.WaitCursor;
int i = txtBody.SelectionStart;
// is regex set?
if (null != find_regex)
{
// searching right to left?
if (next)
match = find_regex.Match (txtBody.Text, i + txtBody.SelectionLength);
else
match = findr_regex.Match (txtBody.Text, i);
if (match.Success)
if (0 == match.Groups[0].Value.Length)
Messagebox.Show ("Incorrect or incomplete Regex pattern:\r\n\r\n\"" + find_string + "\"!");
else
SelectScroll (match.Groups[0].Index, match.Groups[0].Value.Length);
else
NotFound ();
return;
}
string str_find = Unescape (find_string);
StringComparison t;
if (checkBoxMatchCase)
t = StringComparison.CurrentCulture;
else
t = StringComparison.CurrentCultureIgnoreCase;
if (next)
{
i += txtBody.SelectionLength;
i = txtBody.Text.IndexOf (str_find, i, t);
}
else
{
--i;
if (0 <= i)
i = txtBody.Text.LastIndexOf (str_find, i, t);
}
if (i < 0)
NotFound ();
else
SelectScroll (i, str_find.Length);
return;
}
public void NotFound ()
{
Messagebox.Show ("Cannot find \"" + find_string + "\"!");
}
public bool isASCII (char chr)
{
if (' ' <= chr && '~' >= chr)
return true;
return false;
}
// do not change this variable directly!
// use the ReadOnly property
private bool read_only = false;
public bool ReadOnly
{
get
{
return read_only;
}
set
{
txtBody.ReadOnly = read_only = value;
menuFileSave.Enabled =
menuFileForce.Enabled =
menuFileEncryption.Enabled =
menuEditUndo.Enabled =
menuEditCut.Enabled =
menuEditPaste.Enabled =
menuEditDelete.Enabled =
menuEditUppercase.Enabled =
menuEditLowercase.Enabled =
menuEditCapitalize.Enabled =
menuEditTranspose.Enabled =
menuEditReplace.Enabled =
menuEditTimeDate.Enabled =
menuToolsPassword.Enabled =
menuToolsQuickCrypt.Enabled =
menuToolsCR.Enabled =
menuToolsLF.Enabled =
menuToolsDangle.Enabled =
menuToolsCtrl.Enabled =
menuToolsTrim.Enabled =
menuToolsUnwrap.Enabled =
menuToolsWrap.Enabled = !read_only;
}
}
private int CRLF (string s)
{
// Find dangerous dangling CR or LF characters
int i = 0;
for (; ; )
{
int j = s.IndexOf ('\r', i);
i = s.IndexOf ('\n', i);
// is i == -1 and j== -1
if (i == j)
return i;
i++;
// is '\r' followed by '\n'
if (i == j + 2)
continue;
if (0 > j)
return i - 1;
if (0 >= i || j < i)
return j;
return i - 1;
}
}
private int Ctrl (string s, int i)
{
// Find next ANSI Blank characters
for (; i < s.Length; ++i)
{
int j = s[i];
switch (j)
{
default:
break;
case '\x1':
return i;
case '\x1c':
return i;
case '\x1d':
return i;
case '\x1e':
return i;
case '\x1f':
return i;
case '\x81':
return i;
case '\x8d':
return i;
case '\x8f':
return i;
case '\x90':
return i;
case '\x9d':
return i;
case '\xa0':
return i;
}
}
return -1;
}
/// <summary> Select text and scroll into view plus a couple of lines
/// </summary>
/// <param name="index">select start point</param>
/// <param name="length">select length</param>
public void SelectScroll (int index, int length)
{
// first make sure we are visible, if at top
txtBody.Select (index, 0);
txtBody.ScrollToCaret ();
// then make sure there are at least two lines underneath us
int i = txtBody.GetLineFromCharIndex (index);
int lines = txtBody.GetLineFromCharIndex (txtBody.TextLength);
i += 2;
if (i > lines)
i = lines;
i = txtBody.GetFirstCharIndexFromLine (i);
txtBody.Select (i, 0);
txtBody.ScrollToCaret ();
txtBody.Select (index, length);
UpdateStatusBar ();
}
/// <summary> Remember where we were when we started
/// </summary>
public void RememberCaretPosition ()
{
int index = txtBody.SelectionStart;
remember_start.startpoint = txtBody.GetPositionFromCharIndex (index).Y;
remember_start.startline = txtBody.GetLineFromCharIndex (index);
remember_start.startcol = index - txtBody.GetFirstCharIndexFromLine (remember_start.startline);
}
/// <summary> Restore where we were when we started
/// as far as practical
/// </summary>
public void RestoreCaretPosition ()
{
// did the end of the file move up too much?
int last_char = txtBody.Text.Length;
int last = txtBody.GetLineFromCharIndex (last_char);
if (last < remember_start.startline)
remember_start.startline = last;
int index = txtBody.GetFirstCharIndexFromLine (remember_start.startline);
index += remember_start.startcol;
if (last_char < index)
index = last_char;
// test for restoring the y
for (int line = remember_start.startline; line >= 0; --line)
{
txtBody.SelectionStart = txtBody.GetFirstCharIndexFromLine (line);
txtBody.ScrollToCaret ();
if (remember_start.startpoint <= txtBody.GetPositionFromCharIndex (index).Y)
break;
}
txtBody.Select (index, 0);
}
/// <summary> called by Find or Replace to change find_string
/// checks regex flag and sets regex
/// </summary>
/// <param name="flag">checkBoxRegEx status</param>
public bool checkBoxRegEx (bool flag, string s)
{
find_string = s;
find_regex = null;
findr_regex = null;
if (flag)
{
RegexOptions options = RegexOptions.Compiled
| RegexOptions.Multiline;
if (!checkBoxMatchCase)
options |= RegexOptions.IgnoreCase;
try
{
find_regex = new Regex (find_string, options);
}
catch (Exception ex)
{
Messagebox.Show ("Regex Exception: " + ex.Message);
return true;
}
// now set previous search option
options |= RegexOptions.RightToLeft;
// should not fail when other checked?
findr_regex = new Regex (find_string, options);
}
return false;
}
public void comboBoxPatterns_Click (object sender, EventArgs e)
{
ComboBox comboBoxPatterns = (ComboBox) sender;
comboBoxPatterns.DroppedDown = true;
}
/// <summary> called by Find or Replace when the combobox selection changes
/// </summary>
public void comboBoxPatterns_SelectedIndexChanged (object sender, EventArgs e)
{
ComboBox comboBoxPatterns = (ComboBox) sender;
TextBox txtbox = (TextBox) comboBoxPatterns.Tag;
if (0 < comboBoxPatterns.SelectedIndex)
{
string s = (string) comboBoxPatterns.SelectedItem;
txtbox.SelectedText = s.Substring (0, s.IndexOf (' '));
}
comboBoxPatterns.Text = (string) comboBoxPatterns.Items[0];
txtbox.Focus ();
}
#endregion
#region Clipboard escape functions non-Regex
/// <summary> add backslash
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public string Escape (string s)
{
if (null == s)
return null;
s = s.Replace ("\\", "\\\\");
s = s.Replace ("\n", "\\n");
s = s.Replace ("\r", "\\r");
s = s.Replace ("\t", "\\t");
return s;
}
/// <summary> remove backslash
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public string Unescape (string s)
{
if (null == s)
return null;
s = s.Replace ("\\\\", "\x0");
s = s.Replace ("\\n", "\n");
s = s.Replace ("\\r", "\r");
s = s.Replace ("\\t", "\t");
s = s.Replace ("\x0", "\\");
return s;
}
#endregion
}
}