読者です 読者をやめる 読者になる 読者になる

Natural Software

KinectなどのDepthセンサーを中心に活動しています

コメントの話

プログラム

僕の書いているコードはこんなんだよ、っていうノリで見てあげてください。
twitter 見てて、なるほどと思ったので、他意はありませぬ。

コメント不要論原理主義者は人間の言葉なんか使わずにコードのみで主張するべき。

道化師 on Twitter: "コメント不要論原理主義者は人間の言葉なんか使わずにコードのみで主張するべき。 [電波注意]"


先日のコメントについての話に対するご意見をいただきました。
「いいから黙ってコメント書け」という話 - miauの避難所

コード(C#)を見直してみた

ラッパーやユーティリティレベルのコードでは関数コメント以外はほぼ0。
アプリレベルのコードでではちらほらコメントを書いている。

実際書いてるソース

TaskTrayTrac のチケット作成画面を例にして、あとはコード見て^^;

一応コードも載せておくけど長いよ。。。

Ticket.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CookComputing.XmlRpc;

namespace BTS.Trac
{
    /// <summary>
    /// チケット
    /// </summary>
    public class Ticket
    {
        /// <summary>
        /// チケット属性
        /// </summary>
        class TicketAttributes
        {
            public const string SUMMARY = "summary";            // 概要
            public const string DESCRIPTION = "description";    // 説明
            public const string TYPE = "type";                  // 分類
            public const string REPORTER = "reporter";          // 報告者
            public const string OWNER = "owner";                // 担当者
            public const string CC = "cc";                      // 関係者
            public const string PRIORITY = "priority";          // 優先度
            public const string STATUS = "status";              // 状態
            public const string COMPONENT = "component";        // コンポーネント
            public const string MILESTONE = "milestone";        // マイルストーン
            public const string VERSION = "version";            // バージョン
            public const string KEYWORDS = "keywords";          // キーワード
            public const string RESOLUTION = "resolution";      // 解決方法
            public const string DUE_ASSIGN = "due_assign";      // 開始予定日
            public const string DUE_CLOSE = "due_close";        // 終了予定日
            public const string COMPLETE = "complete";          // 進捗率
        }

        #region フィールド

        /// <summary>
        /// 属性
        /// </summary>
        XmlRpcAttributes Attributes = new XmlRpcAttributes();

        /// <summary>
        /// ID
        /// </summary>
        public int ID
        {
            get;
            private set;
        }

        /// <summary>
        /// 作成年月日
        /// </summary>
        public DateTime CreateTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 更新年月日
        /// </summary>
        public DateTime ModifiedTime
        {
            get;
            private set;
        }

        /// <summary>
        /// 概要
        /// </summary>
        public string Summary
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.SUMMARY );
            }

            set
            {
                Attributes.Set( TicketAttributes.SUMMARY, value );
            }
        }

        /// <summary>
        /// 詳細
        /// </summary>
        public string Description
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.DESCRIPTION );
            }

            set
            {
                Attributes.Set( TicketAttributes.DESCRIPTION, value );
            }
        }

        /// <summary>
        /// 分類
        /// </summary>
        public string Type
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.TYPE );
            }

            set
            {
                Attributes.Set( TicketAttributes.TYPE, value );
            }
        }

        /// <summary>
        /// 報告者
        /// </summary>
        public string Reporter
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.REPORTER );
            }

            set
            {
                Attributes.Set( TicketAttributes.REPORTER, value );
            }
        }

        /// <summary>
        /// 担当者
        /// </summary>
        public string Owner
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.OWNER );
            }

            set
            {
                Attributes.Set( TicketAttributes.OWNER, value );
            }
        }

        /// <summary>
        /// 関係者
        /// </summary>
        public string Cc
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.CC );
            }

            set
            {
                Attributes.Set( TicketAttributes.CC, value );
            }
        }

        /// <summary>
        /// 優先度
        /// </summary>
        public string Priority
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.PRIORITY );
            }

            set
            {
                Attributes.Set( TicketAttributes.PRIORITY, value );
            }
        }

        /// <summary>
        /// ステータス
        /// </summary>
        public string Status
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.STATUS );
            }

            set
            {
                Attributes.Set( TicketAttributes.STATUS, value );
            }
        }

        /// <summary>
        /// コンポーネント
        /// </summary>
        public string Component
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.COMPONENT );
            }

            set
            {
                Attributes.Set( TicketAttributes.COMPONENT, value );
            }
        }

        /// <summary>
        /// マイルストーン
        /// </summary>
        public string Milestone
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.MILESTONE );
            }

            set
            {
                Attributes.Set( TicketAttributes.MILESTONE, value );
            }
        }

        /// <summary>
        /// バージョン
        /// </summary>
        public string Version
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.VERSION );
            }

            set
            {
                Attributes.Set( TicketAttributes.VERSION, value );
            }
        }

        /// <summary>
        /// キーワード
        /// </summary>
        public string Keywords
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.KEYWORDS );
            }

            set
            {
                Attributes.Set( TicketAttributes.KEYWORDS, value );
            }
        }

        /// <summary>
        /// 解決方法
        /// </summary>
        public string Resolution
        {
            get
            {
                return Attributes.Get<string>( TicketAttributes.RESOLUTION );
            }

            set
            {
                Attributes.Set( TicketAttributes.RESOLUTION, value );
            }
        }

        /// <summary>
        /// カスタムフィールドの取得
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T GetField<T>( string key )
        {
            return Attributes.Get<T>( key );
        }

        /// <summary>
        /// カスタムフィールドの設定
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetCustomField<T>( string key, T value )
        {
            Attributes.Set( key, value );
        }

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Ticket()
        {
            Initialize();
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="id">取得するチケットID</param>
        public Ticket( int id )
        {
            Get( id );
        }

        /// <summary>
        /// 初期化
        /// </summary>
        private void Initialize()
        {
            ID = 0;
            Attributes.value = new XmlRpcStruct();
        }

        /// <summary>
        /// 指定されたチケットが遷移できるワークフローの状態を取得する
        /// </summary>
        /// <returns>遷移できる状態</returns>
        public string[] GetAvailableActions()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            return Trac.Rpc.ticketGetAvailableActions( ID );
        }

        /// <summary>
        /// 指定されたIDのチケットを取得
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public void Get( int id )
        {
            object[] ret = Trac.Rpc.ticketGet( id );
            Parse( ret );
        }

        /// <summary>
        /// チケットの作成
        /// </summary>
        public void Create()
        {
            if ( string.IsNullOrEmpty( Summary ) ) {
                throw new Exception( "概要が設定されていません" );
            }

            ID = Trac.Rpc.ticketCreate( Summary, Description, Attributes.value );
        }

        /// <summary>
        /// チケットの更新
        /// </summary>
        /// <param name="comment">更新コメント</param>
        public void Update( string comment )
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            object[] ret = Trac.Rpc.ticketUpdate( ID, comment, Attributes.value );
            Parse( ret );
        }

        /// <summary>
        /// チケットの削除
        /// </summary>
        public void Delete()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            Deleet( ID );
            Initialize();
        }

        /// <summary>
        /// 指定されたチケットの更新履歴を取得する
        /// </summary>
        /// <returns>更新履歴</returns>
        public List<TicketChangeLog> ChangeLog()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            object[] ret = Trac.Rpc.ticketChangeLog( ID );
            List<TicketChangeLog> TicketChangeLogs = new List<TicketChangeLog>( ret.Length );
            foreach ( object log in ret ) {
                TicketChangeLogs.Add( new TicketChangeLog( log as object[] ) );
            }

            return TicketChangeLogs;
        }

        /// <summary>
        /// 添付ファイルリストを取得する
        /// </summary>
        /// <returns>添付ファイルリスト</returns>
        public TicketAttachment[] ListAttachments()
        {
            if ( ID == 0 ) {
                throw new Exception( "チケットが指定されていません" );
            }

            object[] ret = Trac.Rpc.ticketListAttachments( ID );

            // データを展開
            TicketAttachment[] attachments = new TicketAttachment[ret.Length];
            for ( int i = 0; i < attachments.Length; ++i ) {
                attachments[i] = new TicketAttachment( this, ret[i] as object[] );
            }

            return attachments;
        }

        /// <summary>
        /// チケットのパース
        /// </summary>
        /// <param name="param">ticket.get,ticket.update の戻り値</param>
        private void Parse( object[] param )
        {
            ID = (int)param[0];
            CreateTime = (DateTime)param[1];
            ModifiedTime = (DateTime)param[2];
            Attributes.value = (XmlRpcStruct)param[3];
        }

        #region 静的メソッド
        /// <summary>
        /// 指定されたクエリのチケットIDを取得
        /// </summary>
        /// <param name="qstr"></param>
        /// <returns></returns>
        public static int[] Query( string qstr )
        {
            return Trac.Rpc.ticketQuery( qstr );
        }

        /// <summary>
        /// 指定日以降に変更されたチケットを取得する
        /// </summary>
        /// <param name="since"></param>
        /// <returns></returns>
        public static int[] GetRecentChanges( DateTime since )
        {
            return Trac.Rpc.ticketGetRecentChanges( since );
        }

        /// <summary>
        /// チケットの削除
        /// </summary>
        /// <param name="id">削除するID</param>
        public static void Deleet( int id )
        {
            Trac.Rpc.ticketDelete( id );
        }

        /// <summary>
        /// チケットフィールドの取得
        /// </summary>
        /// <returns></returns>
        public static XmlRpcStruct[] GetTicketFields()
        {
            XmlRpcStruct[] ret = Trac.Rpc.ticketGetTicketFields();
            return ret;
        }
        #endregion
    }
}
FormTicket.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BTS.Trac;
using CookComputing.XmlRpc;

namespace BTS.Trac.Forms
{
    using TicketType = TracNameAndValueField<ITicketType>;
    using Severity = TracNameAndValueField<ISeverity>;
    using Priority = TracNameAndValueField<IPriority>;

    /// <summary>
    /// チケットの作成/編集画面
    /// </summary>
    public partial class FormTicket : Form
    {
        /// <summary>
        /// チケットのフィールド
        /// </summary>
        XmlRpcStruct[] TicketFields = null;

        /// <summary>
        /// カスタムフィールド
        /// </summary>
        List<TicketCustomField> CustomField = new List<TicketCustomField>();

        /// <summary>
        /// 分類
        /// </summary>
        string[] AllTicketTypeNames = null;

        /// <summary>
        /// 優先度
        /// </summary>
        string[] AllPriorityNames = null;

        /// <summary>
        /// マイルストーン
        /// </summary>
        string[] AllMilestoneNames = null;

        /// <summary>
        /// コンポーネント
        /// </summary>
        string[] AllComponentNames = null;

        /// <summary>
        /// バージョン
        /// </summary>
        string[] AllVersionNames = BTS.Trac.Version.GetAll();

        /// <summary>
        /// 重要度
        /// </summary>
        string[] AllSeverityNames = Severity.GetAll();

        /// <summary>
        /// チケット
        /// </summary>
        public Ticket NewTicket
        {
            get;
            private set;
        }

        /// <summary>
        /// プロジェクト名称
        /// </summary>
        private string ProjectName;

        /// <summary>
        /// 作成/編集
        /// </summary>
        bool IsCreate = true;

        /// <summary>
        /// 更新履歴
        /// </summary>
        private List<TicketChangeLog> ChangeLogs = null;

        /// <summary>
        /// グループごとのコメント
        /// </summary>
        private Dictionary<int, string> Commnet = new Dictionary<int,string>();

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public FormTicket( string ProjectName )
        {
            InitializeComponent();

            NewTicket = new Ticket();
            IsCreate = true;
            this.ProjectName = ProjectName;

            Text = this.ProjectName + " : チケットの作成";

            buttonCreate.Text = "作成(&S)";
            buttonCreateNext.Enabled = true;
            buttonCreateNext.Visible = true;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="ticket">編集するチケット</param>
        public FormTicket( Ticket ticket, string ProjectName )
            : this( ProjectName )
        {
            NewTicket = ticket;
            IsCreate = false;

            Text = this.ProjectName + " : チケットの編集";

            buttonCreate.Text = "編集(&E)";
            buttonCreateNext.Enabled = false;
            buttonCreateNext.Visible = false;
        }

        private void FormTicket_Load( object sender, EventArgs e )
        {

        }

        #region 画面表示処理
        /// <summary>
        /// 表示初期化
        /// </summary>
        /// <param name="IsSetDefaultValue">選択をデフォルト値にするか(作成と編集で変える)</param>
        private void Initialize()
        {
            // カスタムフィールドの表示位置
            Point[] CaptionBase = new Point[2] { 
                    new Point( 4, 403 ),
                    new Point( 273, 403 ),
                };
            Point[] ValueBase = new Point[2] {
                    new Point( 105, 400 ),
                    new Point( 379, 400 ),
                };

            // 入力項目の初期化
            textBoxSummary.Text = "";
            textBoxDescription.Text = "";
            textBoxKeyword.Text = "";
            textBoxCC.Text = "";
            textBoxOwner.Text = "";

            // 報告者
            textBoxReporter.Text = Trac.UserName;

            // 分類
            comboBoxType.Items.Clear();
            foreach ( string name in AllTicketTypeNames ) {
                comboBoxType.Items.Add( name );
            }

            // 優先度
            comboBoxPriority.Items.Clear();
            foreach ( string name in AllPriorityNames ) {
                comboBoxPriority.Items.Add( name );
            }

            // マイルストーン
            comboBoxMilestone.Items.Clear();
            foreach ( string name in AllMilestoneNames ) {
                comboBoxMilestone.Items.Add( name );
            }

            // コンポーネント
            comboBoxComponent.Items.Clear();
            foreach ( string name in AllComponentNames ) {
                comboBoxComponent.Items.Add( name );
            }

            // バージョン
            comboBoxVersion.Items.Clear();
            foreach ( string name in AllVersionNames ) {
                comboBoxVersion.Items.Add( name );
            }

            // 重要度
            comboBoxSeverity.Items.Clear();
            foreach ( string name in AllSeverityNames ) {
                comboBoxSeverity.Items.Add( name );
            }

            // デフォルト値の設定
            foreach ( XmlRpcStruct field in TicketFields ) {
                string fieldName = field["name"].ToString();
                if ( fieldName == "type" ) {
                    SetDefaultValue( field, ref comboBoxType );
                }
                else if ( fieldName == "milestone" ) {
                    SetDefaultValue( field, ref comboBoxMilestone );
                }
                else if ( fieldName == "component" ) {
                    SetDefaultValue( field, ref comboBoxComponent );
                    // コンポーネントに関連連れられている担当者を設定
                    SetOwner( field["value"].ToString() );
                }
                else if ( fieldName == "version" ) {
                    SetDefaultValue( field, ref comboBoxVersion );
                }
                else if ( fieldName == "severity" ) {
                    SetDefaultValue( field, ref comboBoxSeverity );
                }
                else if ( fieldName == "priority" ) {
                    SetDefaultValue( field, ref comboBoxPriority );
                }

                // カスタムフィールドの作成
                if ( field.Contains( "custom" ) ) {
                    int controlWidth = 25;
                    int columun =  (CustomField.Count & 1) == 0 ? 0 : 1;
                    int raw = (CustomField.Count / 2) * controlWidth;

                    // カスタムフィールドの作成
                    XmlRpcAttributes attributes = new XmlRpcAttributes( field );
                    TicketCustomField customField = new TicketCustomField( attributes );
                    CustomField.Add( customField );

                    customField.Caption.Location = new Point( CaptionBase[columun].X, CaptionBase[columun].Y + raw );
                    customField.ValueControl.Location = new Point( ValueBase[columun].X, ValueBase[columun].Y + raw );

                    // コントロールを登録
                    tabPageTicketBaseInfo.Controls.Add( customField.Caption );
                    tabPageTicketBaseInfo.Controls.Add( customField.ValueControl );

                    // コントロールを登録したらフォームたちを下に伸ばす
                    if ( columun == 0 ) {
                        // フォームを下に伸ばす
                        Height += controlWidth;

                        // タブを下に伸ばす
                        tabControlTicketInformation.Height +=  controlWidth;

                        // ボタンを下にずらす
                        buttonCreateNext.Top += controlWidth;
                        buttonCreate.Top += controlWidth;
                        buttonCancel.Top += controlWidth;
                    }
                }
            }
        }

        /// <summary>
        /// 更新するチケット情報を設定
        /// </summary>
        private void UpdateEditTicket()
        {
            Text = this.ProjectName + " : チケットの編集 : #" + NewTicket.ID.ToString() + " (" + NewTicket.Summary + ")";

            // コンボボックスの選択項目
            if ( !string.IsNullOrEmpty( NewTicket.Type ) ) {
                int index = comboBoxType.Items.IndexOf( NewTicket.Type );
                if ( index != -1 ) {
                    comboBoxType.SelectedIndex = index;
                }
            }

            if ( !string.IsNullOrEmpty( NewTicket.Milestone ) ) {
                int index = comboBoxMilestone.Items.IndexOf( NewTicket.Milestone );
                if( index != -1 ) {
                    comboBoxMilestone.SelectedIndex = index;
                }
            }

            if ( !string.IsNullOrEmpty( NewTicket.Component ) ) {
                int index = comboBoxComponent.Items.IndexOf( NewTicket.Component );
                if ( index != -1 ) {
                    comboBoxComponent.SelectedIndex = index;
                }
            }

            if ( !string.IsNullOrEmpty( NewTicket.Version ) ) {
                int index = comboBoxVersion.Items.IndexOf( NewTicket.Version );
                if ( index != -1 ) {
                    comboBoxVersion.SelectedIndex = index;
                }
            }

            if ( !string.IsNullOrEmpty( NewTicket.Priority ) ) {
                int index = comboBoxPriority.Items.IndexOf( NewTicket.Priority );
                if ( index != -1 ) {
                    comboBoxPriority.SelectedIndex = index;
                }
            }

            // 入力項目の設定
            textBoxSummary.Text = NewTicket.Summary;
            textBoxDescription.Text = NewTicket.Description.Replace( "\n", "\r\n" );
            textBoxKeyword.Text = NewTicket.Keywords;
            textBoxCC.Text = NewTicket.Cc;
            textBoxReporter.Text = NewTicket.Reporter;
            textBoxOwner.Text = NewTicket.Owner;

            // カスタムフィールドに値を反映
            foreach ( TicketCustomField field in CustomField ) {
                string value = NewTicket.GetField<string>( field.Name );
                if ( !string.IsNullOrEmpty( value ) ) {
                    field.Value = value;
                }
            }
        }

        /// <summary>
        /// 更新履歴の表示
        /// </summary>
        private void UpdateChangeLog()
        {
            // 作成&更新日時を表示
            labelCreateDate.Text = NewTicket.CreateTime.ToString();
            labelLastModify.Text = NewTicket.ModifiedTime.ToString();

            // チェンジログを表示
            foreach ( TicketChangeLog log in ChangeLogs ) {
                ListViewGroup group = new ListViewGroup( log.Modified.ToString() );
                // グループに登録されていなければ登録する
                int groupIndex = 0;
                for ( groupIndex = 0; groupIndex < listViewChangeLog.Groups.Count; ++groupIndex ) {
                    if ( listViewChangeLog.Groups[groupIndex].Header == group.Header ) {
                        break;
                    }
                }
                if ( groupIndex == listViewChangeLog.Groups.Count ) {
                    groupIndex = listViewChangeLog.Groups.Add( group );
                }

                // コメントを保存
                if ( log.Name == "comment" ) {
                    Commnet.Add( groupIndex, log.After );
                }
                // ほかはアイテムとして表示
                else {
                    // アイテムを登録
                    ListViewItem item = new ListViewItem( log.Name );
                    item.SubItems.Add( log.Before );
                    item.SubItems.Add( log.After );
                    item.Group = listViewChangeLog.Groups[groupIndex];

                    listViewChangeLog.Items.Add( item );
                }
            }

            // 更新がコメントだけの場合はコメントだけという文言を追加しておく
            // でないと、グループが表示されない
            foreach ( ListViewGroup group in listViewChangeLog.Groups ) {
                if ( group.Items.Count == 0 ) {
                    ListViewItem item = new ListViewItem( "コメントのみ" );
                    item.Group = group;

                    listViewChangeLog.Items.Add( item );
                }
            }
        }
        #endregion 

        /// <summary>
        /// チケットの作成
        /// </summary>
        private void CreateTicket()
        {
            string summary = textBoxSummary.Text;
            if ( string.IsNullOrEmpty( summary ) ) {
                throw new Exception( "概要を入力してください" );
            }

            // チケットの作成
            if ( NewTicket == null ) {
                NewTicket = new Ticket();
            }

            NewTicket.Summary = summary;
            NewTicket.Description = textBoxDescription.Text.Replace( "\r\n", "\n" );
            NewTicket.Reporter = textBoxReporter.Text;
            NewTicket.Owner = textBoxOwner.Text;

            NewTicket.Type = comboBoxType.Text;
            NewTicket.Priority = comboBoxPriority.Text;
            NewTicket.Milestone = comboBoxMilestone.Text;
            NewTicket.Component = comboBoxComponent.Text;
            NewTicket.Version = comboBoxVersion.Text;
            // 重要度がない

            NewTicket.Keywords = textBoxKeyword.Text;
            NewTicket.Cc = textBoxCC.Text;

            // カスタムフィールドの登録
            foreach ( TicketCustomField field in CustomField ) {
                NewTicket.SetCustomField( field.Name, field.Value );
            }

            if ( IsCreate ) {
                NewTicket.Create();

                MessageBox.Show( "チケットが作成されました\nID : " + NewTicket.ID, "チケットの作成", MessageBoxButtons.OK, MessageBoxIcon.Information );
            }
            else {
                NewTicket.Update( "Update from TaskTrayTrac" );

                MessageBox.Show( "チケットが更新されました\nID : " + NewTicket.ID, "チケットの更新", MessageBoxButtons.OK, MessageBoxIcon.Information );
            }
        }

        #region ボタン押下処理
        /// <summary>
        /// 作成ボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonCreate_Click( object sender, EventArgs e )
        {
            try {
                CreateTicket();

                DialogResult = DialogResult.OK;
                Close();
            }
            catch ( Exception ex ) {
                MessageBox.Show( ex.Message, "チケット作成/編集", MessageBoxButtons.OK, MessageBoxIcon.Error );
            }
        }

        /// <summary>
        /// 作成して次へボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonCreateNext_Click( object sender, EventArgs e )
        {
            try {
                CreateTicket();
                Initialize();
            }
            catch ( Exception ex ) {
                MessageBox.Show( ex.Message, "チケット作成/編集", MessageBoxButtons.OK, MessageBoxIcon.Error );
            }
        }

        /// <summary>
        /// キャンセルボタン
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonCancel_Click( object sender, EventArgs e )
        {

        }
        #endregion

        #region コンボボックス系の処理
        /// <summary>
        /// コンボボックスの選択が変わったら呼ばれる
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBoxComponent_SelectedIndexChanged( object sender, EventArgs e )
        {
            SetOwner( comboBoxComponent.SelectedItem.ToString() );
        }

        /// <summary>
        /// デフォルト値をコンボボックスに設定
        /// </summary>
        /// <param name="field"></param>
        /// <param name="combo"></param>
        private void SetDefaultValue( XmlRpcStruct field, ref ComboBox combo )
        {
            string value = field["value"].ToString();
            if ( !string.IsNullOrEmpty( value ) ) {
                combo.SelectedIndex = combo.Items.IndexOf( value );
            }
        }

        /// <summary>
        /// コンポーネントに関連付けられている担当者を設定
        /// </summary>
        /// <param name="componentName"></param>
        private void SetOwner( string componentName )
        {
            if ( !string.IsNullOrEmpty( componentName ) ) {
                BTS.Trac.Component defaultComponent = new BTS.Trac.Component( componentName );
                textBoxOwner.Text = defaultComponent.Owner;
            }
        }
        #endregion

        /// <summary>
        /// 説明のタブが切り替わった
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tabControlDiscription_SelectedIndexChanged( object sender, EventArgs e )
        {
            try {
                TabControl tab = sender as TabControl;
                if ( (tab != null) && (tab.SelectedIndex == 1) ) {
                    string text = textBoxDescription.Text;
                    webBrowserPreview.DocumentText = Wiki.WikiToHtml( text.Replace( "\r\n", "\n" ) );
                }
            }
            finally {
            }
        }

        #region チケット情報の更新処理
        /// <summary>
        /// 表示直前で呼ばれる
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormTicket_Shown( object sender, EventArgs e )
        {
            // 作成の場合は更新履歴は表示しない
            if ( IsCreate ) {
                tabControlTicketInformation.Controls.Remove( tabPageTicketModifyInfo );
            }

            // 表示無効
            Enabled = false;

            // 表示処理を開始する
            backgroundWorker.RunWorkerAsync();
        }

        /// <summary>
        /// スレッド処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker_DoWork( object sender, DoWorkEventArgs e )
        {
            try {
                // チケットフィールドの更新
                TicketFields = Ticket.GetTicketFields();
                AllTicketTypeNames = TicketType.GetAll();
                AllPriorityNames = Priority.GetAll();
                AllMilestoneNames = Milestone.GetAll();
                AllComponentNames = BTS.Trac.Component.GetAll();
                AllVersionNames = BTS.Trac.Version.GetAll();
                AllSeverityNames = Severity.GetAll();

                // 編集の場合は更新履歴を取得
                if ( !IsCreate ) {
                    ChangeLogs = NewTicket.ChangeLog();
                }
            }
            catch {
            }
        }

        /// <summary>
        /// スレッド処理の終了
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e )
        {
            try {
                // 表示の初期化
                Initialize();

                // 更新
                if ( !IsCreate ) {
                    UpdateEditTicket();
                    UpdateChangeLog();
                }
            }
            catch ( Exception ex ) {
                MessageBox.Show( ex.Message, "チケット作成/編集", MessageBoxButtons.OK, MessageBoxIcon.Error );
            }
            finally {
                Enabled = true;
            }
        }
        #endregion

        /// <summary>
        /// 選択が変わった
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listViewChangeLog_SelectedIndexChanged( object sender, EventArgs e )
        {
            // 選択されたアイテムのグループ(日付)のコメントを表示する
            if ( listViewChangeLog.SelectedItems.Count != 0 ) {
                int groupIndex = listViewChangeLog.Groups.IndexOf( listViewChangeLog.SelectedItems[0].Group );
                if ( groupIndex != -1 ) {
                    textBoxComment.Text = Commnet[groupIndex].Replace( "\n", "\r\n" );
                }
            }
        }
    }
}