C#.NETメモ:タブブラウザの作成(IEブックマークツリークラス)

【項目】
○タブブラウザの作成:概要
.織峇浜クラス
▲屮薀Ε競據璽献ラス
ブラウザコントロールクラス
IEブックマークツリークラス

概要のページにプロジェクトごとzipで置いてあります。
※この記事の一番下にもこのクラス全体のソースは記載しています。





最後にお気に入り機能です。
ここではTreeViewクラスを継承したMyBookmarkTreeクラスという自作クラスを用いています。



MyBookmarkTreeクラスにはTreeViewクラスの機能に下記の機能を追加しています。

【Privateメソッド】
.張蝓爾縫痢璽匹鮴瀉
bool SetNodes()
⊃謄痢璽匹忙劵痢璽匹鯆媛
bool SetChildNodes()
新規ノードを作成
TreeNode newTreeNode()
ぅ僖垢らフォルダ名・ファイル名を取得
string getFileName()
ゥ痢璽匹棒瀋蠅気譴URLファイルの内容でWebページを開く
bool OpenPage()
Ε痢璽匹選択された場合の処理
bool SelectNode()
Д痢璽匹棒瀋蠅気譴織僖垢鮑鐔する
bool FileDelete()

【イベントハンドラ】
.痢璽疋リックイベント
BookmarkTree_NodeMouseClick
▲侫ルダ内の作成イベント
FileStytemWatcher_Created
フォルダ内の変更イベント
FileStytemWatcher_Changed
ぅ侫ルダ内の削除イベント
FileStytemWatcher_Deleted
ゥ侫ルダ内のリネームイベント
FileStytemWatcher_Renamed
Ε瓮縫紂執猝棔岾く」クリックイベント
MenuOpen_Click
Д瓮縫紂執猝棔嶌鐔」クリックイベント
MenuDelete_Click



MyBookmarkTreeクラスはIEのお気に入りをTreeViewに表示させるためのクラスです。
ツリーに表示された項目をクリックすると、タブブラウザ(MyTabBrowserクラス)に新規タブを追加して表示します。

ツリーにIEのお気に入りを表示する方法ですが、
まずIEのお気に入りを保存しているフォルダを取得します。
お気に入り等の特殊フォルダはEnvironment.GetFolderPath()から取得することができます。
お気に入りのフォルダを取得できたら、お気に入り内のフォルダとファイルを取得し、ツリーに設置します。

        //コンストラクタ
        public MyBookmarkTree(MyTabBrowser rTabBrowser)
        {
            try
            {
                prTabBrowser = rTabBrowser;
                //----------------------------------
                //IEお気に入りフォルダパスを取得
                prBookmarkDirPath = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);
                //----------------------------------
                //IEお気に入りをツリーに設置
                if (!SetNodes(prBookmarkDirPath))
                {
                    //エラー
                }
                //----------------------------------
                //==イベント設定==
                //ノードクリックイベント
                this.NodeMouseClick += new TreeNodeMouseClickEventHandler(BookmarkTree_NodeMouseClick);
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }

お気に入り内のフォルダとファイルを取得し、ツリーに設置する方法ですが、
ここでは再帰処理を用いて、幾重の階層のフォルダとファイルを探しに行っています。

フォルダ内のサブフォルダはDirectory.GetDirectories()で配列として取得できます。
フォルダ内のファイルはDirectory.GetFiles()で配列として取得できます。

フォルダやファイルのパスが取得できたら、TreeNodeクラスのTagにパスを保存させます。
また、ノード(TreeNode)の名前にはURLファイルの名前を設置しています。

       //ツリーにノードを設置
        private bool SetNodes(string vDirPath)
        {
            bool wRet = true;
            try
            {
                //ツリーのノードをクリアする
                this.Nodes.Clear();
                //------------------------------------
                //ルートノード作成
                TreeNode wRootNode = new TreeNode("ルート");
                //フォルダの階層
                int wNodeLv = 0;
                //----------------------------------
                //IEお気に入りをルートノードに配置
                wNodeLv = SetChildNodes(wRootNode, vDirPath, wNodeLv);
                //ルートノードの子ノードをツリーに配置
                foreach(TreeNode wNode in wRootNode.Nodes)
                {
                    //ツリーにノードを配置
                    this.Nodes.Add(wNode);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wRet = false;
            }
            return wRet;
        }
        //親ノードに子ノードを追加する
        private int SetChildNodes(TreeNode rNode, string vDirPath, int vNodeLv)
        {
            try
            {
                TreeNode wNode;
                //親フォルダ内にフォルダノードを作成
                foreach (string wDirPath in Directory.GetDirectories(vDirPath))
                {
                    //親ノードにフォルダノードを追加する
                    wNode = rNode.Nodes.Add(getFileName(wDirPath));
                    //再起処理でフォルダノードの下位ノードを設置する
                    vNodeLv = SetChildNodes(wNode, wDirPath, vNodeLv);
                    //ノードにフォルダパスを格納
                    wNode.Tag = (string)wDirPath;
                }
                //親フォルダ内のファイルノードを作成
                foreach (string wFilePath in Directory.GetFiles(vDirPath, "*.url"))
                {
                    //上位ノードにファイルノードを追加する
                    wNode = rNode.Nodes.Add(getFileName(wFilePath));
                    //ノードにURLファイルパスを格納
                    wNode.Tag = (string)wFilePath;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                vNodeLv = -1;
            }
            return vNodeLv;
        }

ツリーの項目をクリックしてURLファイルのパスを開くためには、
ノードに設定したTagからURLファイルのパスを取得し、そのファイル内にあるURL情報を取得してオープンします。

        //ノードが選択された場合の処理
        private bool SelectNode(TreeNode vNode)
        {
            bool wRet = true;
            try
            {
                //ノードからお気に入りのパスを取得
                string wPath = (string)vNode.Tag;
                //-----------------------------------
                //ファイルの場合
                if (File.Exists(wPath))
                {
                    //WEBページを開く
                    if (!OpenPage(wPath))
                    {
                        //エラー
                    }
                }
                //フォルダの場合
                else if (Directory.Exists(wPath))
                {
                    //ノードが展開している場合
                    if (vNode.IsExpanded)
                    {
                        //ノードの展開を閉じる
                        vNode.Collapse();
                    }
                    //ノードが見展開の場合
                    else
                    {
                        //ノードを展開する
                        vNode.Expand();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wRet = false;
            }
            return wRet;
        }

また、今回のコードではIEお気に入りフォルダ内の監視も行っています。
ファイルの追加・削除・変更があればイベントが発生し、ツリーを再セットするようにしています。
なお、フォルダの監視にはFileSystemWatcherクラスを使っています。

        //コンストラクタ
        public MyBookmarkTree(MyTabBrowser rTabBrowser)
        {
            try
            {
                prTabBrowser = rTabBrowser;
                //----------------------------------
                //IEお気に入りフォルダパスを取得
                prBookmarkDirPath = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);
                //----------------------------------
                //IEお気に入りをツリーに設置
                if (!SetNodes(prBookmarkDirPath))
                {
                    //エラー
                }
                //----------------------------------
                //==フォルダの変更監視==
                prFileStytemWatcher = new FileSystemWatcher(prBookmarkDirPath);
                //監視対象のフォルダ
                prFileStytemWatcher.Path = prBookmarkDirPath;
                //サブフォルダまで監視
                prFileStytemWatcher.IncludeSubdirectories = true;
                //全てを監視
                prFileStytemWatcher.Filter = "";
                //ディレクトリ名、ファイル名、最終更新時間、サイズの変更を監視
                prFileStytemWatcher.NotifyFilter = NotifyFilters.DirectoryName |
                                                   NotifyFilters.FileName |
                                                   NotifyFilters.LastWrite |
                                                   NotifyFilters.Size;
                //監視を開始
                prFileStytemWatcher.EnableRaisingEvents = true;
                //----------------------------------
                //==イベント設定==
                //ノードクリックイベント
                this.NodeMouseClick += new TreeNodeMouseClickEventHandler(BookmarkTree_NodeMouseClick);
                //------------------
                //フォルダ内の作成イベント
                prFileStytemWatcher.Created += new FileSystemEventHandler(FileStytemWatcher_Created);
                //フォルダ内の変更イベント
                prFileStytemWatcher.Changed += new FileSystemEventHandler(FileStytemWatcher_Changed);
                //フォルダ内の削除イベント
                prFileStytemWatcher.Deleted += new FileSystemEventHandler(FileStytemWatcher_Deleted);
                //フォルダ内のリネームイベント
                prFileStytemWatcher.Renamed += new RenamedEventHandler(FileStytemWatcher_Renamed);
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }




【ソースコード】
//MyBookmarkTree.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing;

namespace SimpleTabBrowser
{
    //==クラス定義==
    //IEお気に入りツリークラス
    class MyBookmarkTree:TreeView
    {
        //==デリゲート定義==
        private delegate bool SetNodesDelegate(string vDirPath);
        //---------------------------------------------
        //==Private変数定義==
        //タブブラウザ
        private MyTabBrowser prTabBrowser;
        //IEお気に入りフォルダパス
        private string prBookmarkDirPath;
        //右クリックメニュー
        private ContextMenuStrip prMenu;
        //ファイル変更監視
        FileSystemWatcher prFileStytemWatcher;
        //----------------------------------------------
        //コンストラクタ
        public MyBookmarkTree(MyTabBrowser rTabBrowser)
        {
            try
            {
                prTabBrowser = rTabBrowser;
                //----------------------------------
                //IEお気に入りフォルダパスを取得
                prBookmarkDirPath = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);
                //----------------------------------
                //IEお気に入りをツリーに設置
                if (!SetNodes(prBookmarkDirPath))
                {
                    //エラー
                }
                //----------------------------------
                //==右クリックメニュー==
                prMenu = new ContextMenuStrip();
                ToolStripItem wItem;
                //メニュー項目「開く」を作成
                wItem = prMenu.Items.Add("開く");
                wItem.Click += new EventHandler(MenuOpen_Click);
                //メニュー項目「お気に入りから削除」を作成
                wItem = prMenu.Items.Add("お気に入りから削除");
                wItem.Click += new EventHandler(MenuDelete_Click);
                //----------------------------------
                //==フォルダの変更監視==
                prFileStytemWatcher = new FileSystemWatcher(prBookmarkDirPath);
                //監視対象のフォルダ
                prFileStytemWatcher.Path = prBookmarkDirPath;
                //サブフォルダまで監視
                prFileStytemWatcher.IncludeSubdirectories = true;
                //全てを監視
                prFileStytemWatcher.Filter = "";
                //ディレクトリ名、ファイル名、最終更新時間、サイズの変更を監視
                prFileStytemWatcher.NotifyFilter = NotifyFilters.DirectoryName |
                                                   NotifyFilters.FileName |
                                                   NotifyFilters.LastWrite |
                                                   NotifyFilters.Size;
                //監視を開始
                prFileStytemWatcher.EnableRaisingEvents = true;
                //----------------------------------
                //==イベント設定==
                //ノードクリックイベント
                this.NodeMouseClick += new TreeNodeMouseClickEventHandler(BookmarkTree_NodeMouseClick);
                //------------------
                //フォルダ内の作成イベント
                prFileStytemWatcher.Created += new FileSystemEventHandler(FileStytemWatcher_Created);
                //フォルダ内の変更イベント
                prFileStytemWatcher.Changed += new FileSystemEventHandler(FileStytemWatcher_Changed);
                //フォルダ内の削除イベント
                prFileStytemWatcher.Deleted += new FileSystemEventHandler(FileStytemWatcher_Deleted);
                //フォルダ内のリネームイベント
                prFileStytemWatcher.Renamed += new RenamedEventHandler(FileStytemWatcher_Renamed);
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //----------------------------------------------
        //==Privateメソッド定義==
        //ツリーにノードを設置
        private bool SetNodes(string vDirPath)
        {
            bool wRet = true;
            try
            {
                //ツリーのノードをクリアする
                this.Nodes.Clear();
                //------------------------------------
                //ルートノード作成
                TreeNode wRootNode = new TreeNode("ルート");
                //フォルダの階層
                int wNodeLv = 0;
                //----------------------------------
                //IEお気に入りをルートノードに配置
                wNodeLv = SetChildNodes(wRootNode, vDirPath, wNodeLv);
                //ルートノードの子ノードをツリーに配置
                foreach(TreeNode wNode in wRootNode.Nodes)
                {
                    //ツリーにノードを配置
                    this.Nodes.Add(wNode);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wRet = false;
            }
            return wRet;
        }
        //親ノードに子ノードを追加する
        private int SetChildNodes(TreeNode rNode, string vDirPath, int vNodeLv)
        {
            try
            {
                TreeNode wNode;
                //上位フォルダ内にフォルダノードを作成
                foreach (string wDirPath in Directory.GetDirectories(vDirPath))
                {
                    //親ノードにフォルダノードを追加する
                    wNode = rNode.Nodes.Add(getFileName(wDirPath));
                    //再起処理でフォルダノードの下位ノードを設置する
                    vNodeLv = SetChildNodes(wNode, wDirPath, vNodeLv);
                    //ノードにフォルダパスを格納
                    wNode.Tag = (string)wDirPath;
                }
                //親フォルダ内のファイルノードを作成
                foreach (string wFilePath in Directory.GetFiles(vDirPath, "*.url"))
                {
                    //上位ノードにファイルノードを追加する
                    wNode = rNode.Nodes.Add(getFileName(wFilePath));
                    //ノードにURLファイルパスを格納
                    wNode.Tag = (string)wFilePath;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                vNodeLv = -1;
            }
            return vNodeLv;
        }
        //新規ノードを作成
        private TreeNode newTreeNode(string vPath)
        {
            TreeNode wTreeNode;
            try
            {
                //フォルダ名またはファイル名を取得
                string wName = getFileName(vPath);
                //取得した名称でノードを作成
                wTreeNode = new TreeNode(wName);
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wTreeNode = null;
            }
            //作成したノードを返す
            return wTreeNode;
        }
        //パスからフォルダ名・ファイル名を取得
        private string getFileName(string vFilePath)
        {
            string wName;
            string[] wFileNameArray;
            try
            {
                //パスを¥で区切り配列に格納
                wFileNameArray = vFilePath.Split('¥¥');
                //最下位のフォルダ名またはファイル名を取得
                wName = wFileNameArray[wFileNameArray.Length - 1];
                //ファイル名の.url拡張子をなくす
                wName = wName.Replace(".url", "");
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                //エラーの場合は空白
                wName = "";
            }
            return wName;
        }
        //ノードに設定されたURLファイルの内容でWebページを開く
        private bool OpenPage(string vPath)
        {
            bool wRet = true;
            try
            {
                string wLine;
                string wUrl;
                //ファイルがある場合
                if (File.Exists(vPath))
                {
                    //ファイルを開く
                    StreamReader reader = new StreamReader(vPath, Encoding.Default);
                    //1行ずつ読み込む
                    while (!reader.EndOfStream)
                    {
                        wLine = reader.ReadLine();
                        //「URL=」を含む行を取得
                        if (wLine.IndexOf("URL=") == 0)
                        {
                            //「URL=」を除き、URLを取得する
                            wUrl = wLine.Replace("URL=", "");
                            //タブブラウザにタブを追加
                            prTabBrowser.AddNewTabPage();
                            //指定URLでWebページを開く
                            prTabBrowser.ActiveTabPage.WebBrowser.Navigate(wUrl);
                            //Webページを開いたらループ終了
                            break;
                        }
                    }
                    //ファイルを閉じる
                    reader.Close();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wRet = false;
            }
            return wRet;
        }
        //ノードが選択された場合の処理
        private bool SelectNode(TreeNode vNode)
        {
            bool wRet = true;
            try
            {
                //ノードからお気に入りのパスを取得
                string wPath = (string)vNode.Tag;
                //-----------------------------------
                //ファイルの場合
                if (File.Exists(wPath))
                {
                    //WEBページを開く
                    if (!OpenPage(wPath))
                    {
                        //エラー
                    }
                }
                //フォルダの場合
                else if (Directory.Exists(wPath))
                {
                    //ノードが展開している場合
                    if (vNode.IsExpanded)
                    {
                        //ノードの展開を閉じる
                        vNode.Collapse();
                    }
                    //ノードが見展開の場合
                    else
                    {
                        //ノードを展開する
                        vNode.Expand();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wRet = false;
            }
            return wRet;
        }
        //ノードに設定されたパスを削除する
        private bool FileDelete(string vPath)
        {
            bool wRet = true;
            try 
            {
                string wMsg = "お気に入りから¥n[   " + getFileName(vPath) + "   ]¥nを削除して宜しいですか?";
                if (MessageBox.Show(wMsg, "確認メッセージ",MessageBoxButtons.OKCancel) == DialogResult.OK)
                {
                    if (File.Exists(vPath))
                    {
                        File.Delete(vPath);
                    }
                    else if(Directory.Exists(vPath))
                    {
                        //フォルダ内にフォルダまたはファイルが存在しない場合は削除
                        if (Directory.GetFiles(vPath).Length == 0 && Directory.GetDirectories(vPath).Length == 0)
                        {
                            Directory.Delete(vPath);
                        }
                        else
                        {
                            MessageBox.Show("フォルダ内が空でない場合は、削除できません。");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
                wRet = false;
            }
            return wRet;
        }
        //----------------------------------------------
        //==イベント定義==
        //ノードクリックイベント
        private void BookmarkTree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            try
            {   //左クリックの場合
                if (e.Button == MouseButtons.Left)
                {
                    TreeNode wNode = (TreeNode)e.Node;
                    //-----------------------------------
                    //ノードの選択処理
                    if (!SelectNode(wNode))
                    {
                        //エラー
                    }
                }
                //右クリックの場合
                else if (e.Button == MouseButtons.Right)
                {
                    //マウス位置にあるノードを取得
                    TreeNode wNode = this.GetNodeAt(e.X, e.Y);
                    //取得したノードを選択状態にする
                    this.SelectedNode = wNode;
                    //-----------------------------------
                    //右クリックメニューを表示
                    prMenu.Show(this, new Point(e.X, e.Y));
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //-----------------------
        //メニュー項目「開く」クリックイベント
        private void MenuOpen_Click(object sender, EventArgs e)
        {
            try
            {
                //選択状態のノードを取得
                TreeNode wNode = this.SelectedNode;
                //-----------------------------------
                //ノードの選択処理
                if (!SelectNode(wNode))
                {
                    //エラー
                }
            }

            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //メニュー項目「削除」クリックイベント
        private void MenuDelete_Click(object sender, EventArgs e)
        {
            try
            {
                //選択状態のノードを取得
                TreeNode wNode = this.SelectedNode;
                //-----------------------------------
                if (!FileDelete((string)wNode.Tag))
                {
                    //エラー
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //-----------------------
        //フォルダ内の作成イベント
        private void FileStytemWatcher_Created(object sender, FileSystemEventArgs e)
        {
            try
            {
                if (!this.InvokeRequired)
                {
                    //IEお気に入りをツリーに再設置
                    if (!SetNodes(prBookmarkDirPath))
                    {
                        //エラー
                    }
                }
                else
                {
                    //IEお気に入りをツリーに再設置(監視クラスはスレッドが違う為、Invokeでツリーにスレッドを戻す)
                    this.Invoke(new SetNodesDelegate(SetNodes), prBookmarkDirPath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //フォルダ内の変更イベント
        private void FileStytemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                if (!this.InvokeRequired)
                {
                    //IEお気に入りをツリーに再設置
                    if (!SetNodes(prBookmarkDirPath))
                    {
                        //エラー
                    }
                }
                else
                {
                    //IEお気に入りをツリーに再設置(監視クラスはスレッドが違う為、Invokeでツリーにスレッドを戻す)
                    this.Invoke(new SetNodesDelegate(SetNodes), prBookmarkDirPath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //フォルダ内の削除イベント
        private void FileStytemWatcher_Deleted(object sender, FileSystemEventArgs e)
        {
            try
            {
                if (!this.InvokeRequired)
                {
                    //IEお気に入りをツリーに再設置
                    if (!SetNodes(prBookmarkDirPath))
                    {
                        //エラー
                    }
                }
                else
                {
                    //IEお気に入りをツリーに再設置(監視クラスはスレッドが違う為、Invokeでツリーにスレッドを戻す)
                    this.Invoke(new SetNodesDelegate(SetNodes), prBookmarkDirPath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
        //フォルダ内のリネームイベント
        void FileStytemWatcher_Renamed(object sender, RenamedEventArgs e)
        {
            try
            {
                if (!this.InvokeRequired)
                {
                    //IEお気に入りをツリーに再設置
                    if (!SetNodes(prBookmarkDirPath))
                    {
                        //エラー
                    }
                }
                else
                {
                    //IEお気に入りをツリーに再設置(監視クラスはスレッドが違う為、Invokeでツリーにスレッドを戻す)
                    this.Invoke(new SetNodesDelegate(SetNodes), prBookmarkDirPath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("例外エラー:" + ex.Message);
            }
        }
    }
}

コメント
こ・・・こんなにありがとうございます!!!
こんな超初心者にここまでしていただいて。
ほんとになんとお礼を申し上げたらよいやら・・・。
とりあえず今はちょっとVisual Studioを使える状態ではないので、少ししたら追って状況を報告させていただきます。

あと私のブログにこのブログのリンクを貼らせてもらえないでしょうか?
私のブログにもたまにC#などのことについて書いていますので、いろいろご指摘いただけたら幸いです。
http://axolotl-mexicanum.blog.so-net.ne.jp
  • Axolotl
  • 2009/05/31 9:40 PM
>Axolotlさん
いえいえー
いずれアップしとこうかなと思ってた内容だったので良い機会でしたし(笑

あと分からないところがあれば気軽に聞いてください。
私の答えられる範囲ならお答えしますので。

リンクは自由に貼っていただいて構いませんよ!
貼っていただければ、こちらも貼らせて頂きます。

ところで中学生さんだったんですね!
自分でHTMLやjavascriptとか勉強されたんですか?
凄いですね!

私がプログラム始めたの社会人になってからで、
仕事で経験値上げて何とか今のレベルになりましたが…
それくらいからやってれば、私くらいの歳(二十台半ば)になったら凄いもの作れそうだあ
…良いなあ( ̄▽ ̄*)
  • 謎猫
  • 2009/06/02 1:53 AM
コメントする








    
この記事のトラックバックURL
トラックバック