Multi File Editor

ファイル比較とプリロードを設定して Multi File Editor を完成します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

ファイル比較

  1. 複数のファイルをオープンしているので FileClose() もこれに対応します。
    m_num が閉じようとしているファイルの Index です。
    m_page と tabControl1 に登録されているファイルを削除します。
    PageChenge() で先頭のファイルに切り替えます。
        ArrayList   m_page;         // 編集ページの配列
        int         m_num;          // 現在編集中の m_page
    
        private void FileClose(object sender, EventArgs e)
        {   DialogResult rc;
    
            if (m_num < 0 || m_num >= m_page.Count) return;
            if (m_fclass.flag || ((FileClass)m_page[m_num]).flag)
            {   rc = MessageBox.Show("保存しないで閉じますか", "選択",
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (rc == DialogResult.No) return;
            }
            m_page.RemoveAt(m_num);
            if (m_num<tabControl1.TabPages.Count)
            {   tabControl1.TabPages.RemoveAt(m_num);  }
            m_fclass.flag = false;
            m_num = -1;
            PageChenge(0, -1);
        }
    
  2. TabPage-0 と TabPage-n の TEXT を比較して、異なる箇所に位置づけます。
    TabPage の入れ替えは Swap メニューで行います。
        private void Swap(object sender, EventArgs e)
        {
            if (m_page.Count<2 || m_num==0)   return;
            m_page[m_num] = m_page[0];
            m_page[0]= m_fclass;
            tabControl1.GetControl(m_num).Text = ((FileClass)m_page[m_num]).file;
            tabControl1.GetControl(0).Text = m_fclass.file;
            tabControl1.SelectTab(0);
        }
    
    TabPage-n を選択して F9 キー押すと TEXT 比較が起動します。
        //★ キー操作
        private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
        {
                  ・・・
                case Keys.F9:
                    FileComp();
                    break;
    
    また TabControl が選択されているときにも起動するように、TabKeyDown を設定します。
        // Tab がアクティブのとき、F9(ファイル比較)を押す
        private void TabKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F9)   FileComp();
        }
    
  3. TEXT を比較する FileComp() メソッドです。
    TabPage-n を選択した状態で起動して下さい。
    posn と pos0 に現在のカーソル位置を設定します。
    strn と str0 に比較する TEXT を格納します。
    strn[posn] と str0[pos0] を比較して違いを調べます。
    違いがあった行を PageChenge(pg, posn); で色を設定して表示します。
    比較を続けるときは、次の位置をマウスクリックで設定して F9 で起動します。
        // File を比較
        private void FileComp()
        {
            int     pg, pos0, posn;
            string  str0, strn;
            if (m_page.Count<2 || m_num<1)
            {
                MessageBox.Show("比較対象ページを選択して下さい");
                return;
            }
            pg = m_num;
            posn = ((FileClass)m_page[m_num]).cursor;
            pos0 = ((FileClass)m_page[0]).cursor;
            strn = richTextBox1.Text;
            PageChenge(0, pos0);
            str0 = richTextBox1.Text;
            for (posn++, pos0++; posn < strn.Length && pos0 < str0.Length; posn++, pos0++)
            {
                if (strn[posn] != str0[pos0])
                {  break;  }
            }
            richTextBox1.SelectionStart = pos0;
            ((FileClass)m_page[0]).cursor = pos0;
            ((FileClass)m_page[pg]).cursor = posn;
            PageChenge(pg, posn);
        }
    
  4. 違いがあった行の先頭ワードに色を設定して表示する PageChenge() メソッドです。
    このメソッドは以前にも出てきましたが、下記のメソッドに置き換えて下さい。
    色を設定しないと、違いが生じた行(箇所)が見つけにくくなります。
        private void PageChenge(int newpage, int cursor)
        {
            if (m_page.Count == 0)
            {   richTextBox1.Clear();
                return;
            }
    
            if (newpage!=m_num)     //ページが異なるとき
            {   //現在ページを保存
                if (m_num>=0 && m_num<m_page.Count)
                {
                    m_fclass.Save();
                    m_fclass.cursor = richTextBox1.SelectionStart;
                    m_page[m_num] = m_fclass;
                }
                //m_page[newpage]をロード
                m_num = newpage;
                if (m_num<0 || m_num>=m_page.Count)   m_num = 0;
                m_fclass = (FileClass)m_page[m_num];
                m_fclass.Load();
                tabControl1.SelectTab(m_num);
            }
            m_fclass.SetCurPos(cursor);
            this.Text = m_fclass.path;
            FileClass.WordSelect(m_fclass.cursor, Color.Green);
            richTextBox1.Select(m_fclass.cursor, 0);
        }
    

プリロード

  1. 良く参照する複数のファイルを登録しておいて、メニューから一発でオープンします。
    Editor の目的はプログラムファイルや HTML ファイルを編集することですが、頻繁に参照するファイルは決まっています。
    そこで事前にファイルリストを作成しておいて、メニューから一発でオープンします。
    ファイルを登録する ArrayList m_files です。
    m_files を Form1 の Constructor で初期化して下さい。
    m_files に登録する class FileName は TabPage Chenge を参照して下さい。
        ArrayList   m_files;        // Pre Load File
    
        m_files = new ArrayList();
    
  2. プリロード関係のメソッドはメニューから起動します。
    PreLoad() で登録されているファイルをオープンします。
    PreAdd() で現在オープンされているファイルを追加登録します。
    追加登録では、既に同名のファイルが登録されていれば削除して登録し直します。
    一度削除するのは、カーソル位置を変更したり、順番を入れ替えるためです。
    PreDel() で登録されているファイルを削除します。
    PreCloar() は m_files に登録されている全てのファイルを削除します。
        // m_files[] プリロード
        private void PreLoad(object sender, EventArgs e)
        {
            for (int i = 0; i < m_files.Count; i++)
            {
                LoadFile(((FileName)m_files[i]).path, ((FileName)m_files[i]).code, ((FileName)m_files[i]).cursor);
            }
        }
    
        // 一度削除してから追加
        private void PreAdd(object sender, EventArgs e)
        {
            if (m_num < 0) return;
            PreDel(sender, e);
            FileName fname = new FileName(m_fclass.path, m_fclass.code, m_fclass.cursor);
            m_files.Add(fname);
        }
    
        private void PreDel(object sender, EventArgs e)
        {
            int i;
            for(i=0; i<m_files.Count && ((FileName)m_files[i]).path!=m_fclass.path; i++);
            if (i<m_files.Count)  m_files.RemoveAt(i);
        }
    
        private void PreCloar(object sender, EventArgs e)
        {
            m_files.Clear();
        }
    

XML に保存する

  1. XML ファイルにプリロード関係の情報を含めて保存します。
    XML は XML に保存 を参照して下さい。
    XML にロード(セーブ)する情報を定義する class XmlClass です。
    フォント情報に加えてウインドウサイズとプリロード関係の情報を定義しています。
        public class XmlClass
        {
            public string       name;   //Font Name
            public float        point;  //Font Size
            public int          width;  //ウインドウ幅
            public int          height; //ウインドウ高さ
            public ArrayList    path;   //ファイルパス
            public ArrayList    code;   //TEXTコード
            public ArrayList    cursor; //カーソル位置
        }
    
  2. Form_Load() で XML ファイルから読み込みます。
        private void Form_Load(object sender, EventArgs e)
        {
            if (File.Exists(XML_File))  //ファイルの有無をチェック
            {
                System.Xml.Serialization.XmlSerializer serializer =
                    new System.Xml.Serialization.XmlSerializer(typeof(XmlClass));
                System.IO.FileStream fs =
                    new System.IO.FileStream(XML_File, System.IO.FileMode.Open);
                XmlClass obj = (XmlClass)serializer.Deserialize(fs);
                fs.Close();
                int point = (int)obj.point;
                m_font = new Font(obj.name, point);
                m_width = obj.width;
                m_height = obj.height;
                for(int i=0; i<obj.path.Count; i++)
                {   FileName fname =
                       new FileName((string)obj.path[i], (string)obj.code[i], (int)obj.cursor[i]);
                    m_files.Add(fname);
                }
            }
            else m_font = new Font("MS 明朝", 12);
            richTextBox1.Font = m_font;
            richTextBox1.LanguageOption = RichTextBoxLanguageOptions.UIFonts;
            if (m_width<100)    m_width = 1000;
            if (m_height<100)   m_height = 480;
            this.Width = m_width;
            this.Height = m_height;
            // 4 TAB に設定
            const int EM_SETTABSTOPS = 0x00CB;
            SendMessage(richTextBox1.Handle, EM_SETTABSTOPS, 1, new int[] {16});
        }
    
  3. Form_Closing() で XML ファイルに保存します。
        private void Form_Closing(object sender, FormClosingEventArgs e)
        {
            if (Cancel_Check())
            {
                e.Cancel = true;
                return;
            }
            XmlClass obj = new XmlClass();
            obj.name = m_font.Name;
            obj.point = m_font.SizeInPoints;
            obj.width = this.Width;
            obj.height = this.Height;
            obj.path = new ArrayList();
            obj.code = new ArrayList();
            obj.cursor = new ArrayList();
            for(int i=0; i<m_files.Count; i++)
            {
                obj.path.Add(((FileName)m_files[i]).path);
                obj.code.Add(((FileName)m_files[i]).code);
                obj.cursor.Add(((FileName)m_files[i]).cursor);
            }
            System.Xml.Serialization.XmlSerializer serializer =
                new System.Xml.Serialization.XmlSerializer(typeof(XmlClass));
            System.IO.FileStream fs =
                new System.IO.FileStream(XML_File, System.IO.FileMode.Create);
            serializer.Serialize(fs, obj);
        }
    
  4. これで一応 Multi File Editor の完成です。
    現在のところ順調に動作しているので、後は不具合を見つけて修正するだけです。
    私のページの説明によって生じた損害には一切責任を負いません。
    特に Editor などは大切なファイルを破壊する恐れもあり、十分にデバッグの上使用して下さい。

【NOTE】

  1. BOM(byte order mark)とはテキストファイルの先頭に格納される文字コードを示すIDです。
    文字コード BOM 説明
    utf-16 FFFE 先頭2バイトが BOM です
    utf-8 EFBBBF先頭3バイトが BOM です
    utf-8(BOM無し) BOM はありません
  2. BOM が格納されていると Encoding で間違った設定をしても、文字化けすることなく正しく読むことができます。
    utf-8 には BOM 無しと有りがあるのですが、Windows の utf-16 では BOM 無しのファイルは見かけません。
    文字化けを防ぐ意味からも utf-8 では BOM を設定するほうが良いかも知れません。
  3. TabPage でページを切り替えたとき、何らかのタイミングで修正 TEXT が紛失する現象が起こっています。
    いつも紛失する訳では無く、操作中にふと気が付けば紛失しています。
    この手のエラーは再現性が無く、原因を調べるのも一苦労です。
    紛失を防ぐには、上書き保存してからページを切り替えて下さい。
    または、別々に起動してウインドウを並べて表示します。

[Previous Chapter ↑] FileClass

超初心者のプログラム入門(C# Frame Work)