Memo2 β版

Memo2 β版を作成します。

前田稔の超初心者のプログラム入門

Form 描画関数

  1. Title.Text, TitleBar, TreeView, RichTextBox の描画を整理します。
    Window 項目 フラグ 説明
    Title.Text 日付 0 作成日付 : 更新日付
    TitleBar ファイル名 0 ファイル名を表示します(m_file)
    Node List 1 Top からの NodeList を表示します
    ファイル名+Title 2 ファイル名+Title を表示します(m_sou)
    TreeView TopNode m_Idx=0 TopNode を開きます
    SelectedNode m_Idx>0 m_Idx の Node を開きます
    ExpandAll 4 全 Node を開きます
    RichTextBox TextBox 0 t_txt[m_Idx]; を表示します
    TextBox * 機密保護が設定されています
    TextBox PWORD * & パスワードのとき解読して表示する
  2. 上の表に従って画面表示を行う FormVier(byte flag) 関数を作成します。
    str = (string)t_ymd[m_Idx]; if (str[str.Length-1]=='*') で機密保護の設定を調べています。
    パスワードがタイプ入力されているときは、暗号化されたテキストを decode(msg) で複合化して表示します。
    機密保護に関係する関数は、このページの後半を参照して下さい。
        // TitleBar, Title, TreeView, RichTextBox の描画
        private void FormView(byte flag)
        {
            string  str;
            Title.Text = (string)t_ttl[m_Idx] + "(" + (string)t_ymd[m_Idx] + ")";
            // Title Bar
            switch(flag&3)
            {
                case 0: this.Text = m_file; break;
                case 1:
                    TreeNode tNode = (TreeNode)t_node[m_Idx];
                    FormView(tNode);
                    break;
                case 2: this.Text = m_sou; break;
            }
            // TreeView
            if ((flag&4)==4)    treeView1.ExpandAll();
            if (m_Idx==0)   treeView1.TopNode.Expand();
            else    treeView1.SelectedNode = (TreeNode)t_node[m_Idx];
            // RichTextBox
            str = (string)t_ymd[m_Idx];
            if (str[str.Length-1]=='*')
            {   if (m_pass!="Decode")
                {   richTextBox1.Text = "☆機密保護が設定されています";
                    return;
                }
                str = (string)t_txt[m_Idx];
                char[] msg = str.ToCharArray();
                decode(msg);
                richTextBox1.Text = new string(msg);
            }
            else    richTextBox1.Text = (string)t_txt[m_Idx];
            richTextBox1.Modified = false;
        }
    
  3. Node のルートを Top からたどり Text に表示する関数です。
        private void FormView(TreeNode pnode)
        {
            TreeNode node;
            m_str = "";
            int i;
            for (node = pnode; node != null; node = node.Parent)
            {
                for (i = 0; i < t_node.Count && (TreeNode)t_node[i] != node; i++) ;
                if (i >= t_node.Count) break;
                m_str = t_ttl[i] + "/" + m_str;
            }
            this.Text = m_str;
            treeView1.SelectedNode = pnode;
            treeView1.Select();
        }
    
  4. RichTextBox に str を設定して key の色を変えて表示する関数です。
    この関数は検索(置換)のときにキーワードを目立つように表示します。
        private void FormView(string str, string key)
        {
            int pos;
            FormView((TreeNode)t_node[m_Idx]);
            this.Text = m_str;
            richTextBox1.Text = str;
            richTextBox1.Select(0, richTextBox1.Text.Length);
            richTextBox1.SelectionColor = Color.Black;
            Title.Text = (string)t_ttl[m_Idx] + "(" + (string)t_ymd[m_Idx] + ") " + m_Idx;
            for (pos = 0; ; )
            {
                pos = richTextBox1.Find(key, pos, RichTextBoxFinds.None);
                if (pos < 0) break;
                richTextBox1.SelectionColor = Color.DarkMagenta;
                pos++;
            }
            richTextBox1.Modified = false;
        }
    
  5. TreeView をクリアして ArrayList に従って TreeView を再登録します。
    この関数は TreeView の Node を削除・挿入したときに呼ばれます。
        private void ReTree_Node(int idx)
        {
            t_node.Clear();
            treeView1.Nodes.Clear();
            m_Idx = 0;
            Set_TVFunc(treeView1, 2);       // TreeView を再登録
            m_Idx = 0;
            if (idx < t_node.Count) m_Idx = idx;
            richTextBox1.Text = (string)t_txt[m_Idx];
            TreeNode node = (TreeNode)t_node[m_Idx];
            FormView(node);
            richTextBox1.Modified = false;
            m_UP = true;
        }
    
  6. treeView1_AfterSelect() では CheckText(); に続いて FormView(0); を呼び出します。
        // Tree View の選択が変更された時の処理
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            CheckText();
            for (m_Idx = 0; m_Idx < t_node.Count && (TreeNode)t_node[m_Idx] != e.Node; m_Idx++);
            if (m_Idx < t_node.Count)
            {
                FormView(0);
            }
        }
    
  7. CheckText() 関数は RichTextBox が修正されたときに t_txt[m_Idx] に保存します。
    機密が設定されているテキストが更新されたときは Encode して格納します。
    t_txt[] には暗号化されたテキストデータを保存します。
        // richTextBox1 を調べて ArrayList を更新
        private void CheckText()
        {
            if (richTextBox1.Modified && (m_Idx<t_txt.Count))
            {
                string str = (string)t_ymd[m_Idx];
                if (str[str.Length-1]!='*') t_txt[m_Idx] = richTextBox1.Text;
                else
                {   if (m_pass=="Decode")
                    {
                        // Encode して t_txt[m_Idx] に格納
                        char[] msg = richTextBox1.Text.ToCharArray();
                        encode(msg);
                        t_txt[m_Idx] = new string(msg);
                    }         
                }
                SetDate(m_Idx);
                m_UP = true;
                richTextBox1.Modified = false;
            }
        }
    
  8. SetDate() 関数でも機密の設定(*)を考慮して下さい。
    機密が設定された Node は、日付の後に * を設定します。
        // 更新日付の設定
        private void SetDate(int idx)
        {
            string ymd, str;
            str = DateTime.Now.ToString();
            ymd = (string)t_ymd[idx];
            if (ymd.Length < 20)    // YYMMDD:YYMMDD
            {
                str = "20" + ymd.Substring(0, 2) + "/" + ymd.Substring(2, 2) + "/"
                      + ymd.Substring(4, 2) + ":" + str.Substring(0, 10);
            }
            else    // YYYY/MM/DD:YYYY/MM/DD 
            {   str = ymd.Substring(0, 11) + str.Substring(0, 10); }
            if (ymd[ymd.Length-1]=='*') str = str + "*";
            t_ymd[idx] = str;
        }
    
  9. HelpAbout() 関数からは FormView(0); で呼び出します。
        private void HelpAbout(object sender, EventArgs e)
        {
            MessageBox.Show("Memo2 Program  β版");
            FormView(0);
        }
    
  10. 他にも FormView() 関数に置き換えた方が良い箇所が幾つかあります。
    特に、この次に説明する「機密保護」を設定すると FormView() を使わなければ支障が出る場合があります。
    FormView() 関数では、機密保護を考慮してプログラミングしています。

機密保護の設定

  1. 暗号化するプログラムでは Pointer を使うので unsafe を設定して下さい。
    unsafe の設定は Pointer を使う を参照して下さい。
    パスワードを間違えると文字化けして判読不能のテキストが表示されます。
    パスワードを間違えたときは、保存しないで終了して下さい。
    間違ったパスワードで保存すると、本当に判読不能になることがあります。
    簡単な機密保護のプログラムなので、保証の限りではありません。
  2. パスワードを入力します。
    検索キーのタイプと共通ですが、m_pass = "PASS"; で識別します。
        string      m_pass = "";                // Pass Word 
    
        // Pass Word の設定
        private void PassWord(object sender, EventArgs e)
        {
            m_pass = "PASS"; 
            richTextBox1.Hide();
            button1.Show();
            textBox1.Show();
        }
    
  3. button_Click() でパスワードを入力します。
    検索キーのタイプと共通ですが、m_pass = "PASS"; で識別します。
    パスワードをキーにして Encode と Decode テーブルを設定します。
        private void button_Click(object sender, EventArgs e)
        {
            m_sou = textBox1.Text;
            m_des = textBox2.Text;
            if (m_pass == "PASS")
            {
                uint wk = 0;
                for (int i = 0; i < m_sou.Length; i++) wk += m_sou[i];
                shuffle(wk);
                for (int i = 0; i < 208; i++) m_dt[m_et[i] - 48] = (byte)(i + 48);
                m_pass = "Decode";
            }
            button1.Hide();
            textBox1.Hide();
            textBox2.Hide();
            richTextBox1.Show();
            FormView(1);
        }
    
  4. 機密保護は Node 単位に設定が可能で、現在の Node にパスワードを設定します。
    パスワードはメモファイル毎に設定可能で、ファイルに含まれる全 Node に共通です。
        private void SetPass(object sender, EventArgs e)
        {
            string str = (string)t_ymd[m_Idx];
            if (str[str.Length - 1] == '*')
            {
                MessageBox.Show("機密保護は設定済みです");
                return;
            }
            if (m_pass == "")
            {
                MessageBox.Show("PassWord を設定して下さい");
                return;
            }
            str = str + "*";
            //Console.WriteLine("yymmdd : " + str);
            t_ymd[m_Idx] = str;
            Title.Text = (string)t_ttl[m_Idx] + "(" + str + ")";
            m_UP = true;
            char[] msg = richTextBox1.Text.ToCharArray();
            encode(msg);
            t_txt[m_Idx] = new string(msg);
            richTextBox1.Text = "機密保護を設定しました";
        }
    
  5. Node のパスワードを解除します。
        private void ResetPass(object sender, EventArgs e)
        {
            string str = (string)t_ymd[m_Idx];
            if (str[str.Length - 1] != '*')
            {
                MessageBox.Show("機密保護は設定されていません");
                return;
            }
            if (m_pass == "")
            {
                MessageBox.Show("PassWord を設定して下さい");
                return;
            }
            str = str.Substring(0, str.Length - 1);
            t_ymd[m_Idx] = str;
            Title.Text = (string)t_ttl[m_Idx] + "(" + str + ")";
            str = (string)t_txt[m_Idx];
            char[] msg = str.ToCharArray();
            decode(msg);
            richTextBox1.Text = new string(msg);
            t_txt[m_Idx] = richTextBox1.Text;
            m_UP = true;
        }
    
  6. 乱数関数と暗号化(Encode)のソースです。
        private uint random(uint n)
        { return (n * 5041 + 13); }
    
        private void shuffle(uint val)
        {
            uint i, j;
            for (i = 0; i < 208; i++) m_et[i] = 0;
            for (i = 0, j = val; i < 208; i++)
            {
                for (j = random(j) % 208; m_et[j] != 0; j = (j + 1) % 208) ;
                m_et[j] = (byte)(i + 48);
            }
            for (i = 0; i < 208 && m_et[i] != 64; i++) ;
            m_et[i] = m_et[16];
            m_et[16] = 64;
        }
    
        // Encode
        unsafe private void encode(char[] msg)
        {
            char    chr;
            byte*   byt;
    
            byt = (byte*)&chr;
            for (int i = 0; i < msg.GetLength(0); i++)
            {
                chr = msg[i];
                e_cvt(byt);
                msg[i] = chr;
            }
        }
        // byte 変換
        unsafe private void e_cvt(byte* pt)
        {
            for (int i = 0; i < sizeof(char); i++)
            {
                if (*(pt + i) >= 48) *(pt + i) = m_et[*(pt + i) - 48];
            }
        }
    
  7. 複合化(Decode) のソースです。
    暗号化/複合化の説明は TEXT の暗号化 TEXT の複合化 を参照して下さい。
        // Decode
        unsafe private void decode(char[] msg)
        {
            char    chr;
            byte*   byt;
    
            byt = (byte*)&chr;
            for (int i = 0; i < msg.GetLength(0); i++)
            {
                chr = msg[i];
                d_cvt(byt);
                msg[i] = chr;
            }
        }
        // decode byte 変換
        unsafe private void d_cvt(byte* pt)
        {
            for (int i = 0; i < sizeof(char); i++)
            {
                if (*(pt + i) >= 48) *(pt + i) = m_dt[*(pt + i) - 48];
            }
        }
    

検索メニューの改良

  1. Text を修正した状態で検索を行うと「修正した Text が元に戻る」現象が確認されました。
    プログラムの修正は簡単で、検索を実行する前に CheckText() を読んで ArrayList を更新すれば良いだけです。
        //☆ 検索メニュー
        private void TSearch(object sender, EventArgs e)
        {   CheckText();
              ・・・
        }
        private void Search(object sender, EventArgs e)
        {   CheckText();
              ・・・
        }
        private void Replace(object sender, EventArgs e)
        {   CheckText();
              ・・・
        }
    
  2. Memo2 を使っていて、大変不自由を感じることがありました。
    検索メニューで Text が大きくて該当箇所が見えない時、中断して確認すると最初から検索をやり直さなければならないことです。
    そこで "検索の継続(&N)" メニューを追加することにしました。
  3. 検索メニューに "検索の継続(&N)" を追加して下さい。
    イベントハンドラは簡単で CheckText() を読んでから Next(m_sou) を実行するだけです。
    これで中断したページから引き続いて検索してくれます。
        private void SearchNX(object sender, EventArgs e)
        {   CheckText();
            Next(m_sou);
        }
    
  4. このように簡単にバージョンアップ(修正)できるのも自分で作成したアプリケーションならではのことでしょう。

[Previous Chapter ↑] Memo2 α版

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