MotionPanel.cpp

#include "StdAfx.h"
#include "MotionPanel.h"


#include "Motion.h"
#include "Pose.h"

#include "TextInputDialog.h"


using namespace Core;
using namespace System;
using namespace System::Diagnostics;


/// [ Constructor ]
MotionPanel::MotionPanel( void )
{
    // コンポーネントを初期化する
    InitializeComponent();


    // 時間トラックバーの目盛りを 1[ sec ]ごとに設定する
    this->m_timeTrackBar->TickFrequency
        = safe_cast< int >( 1.0 / Core::Motion::UnitTime );

    // タイマのインターバル[ msec ]を モーションの単位時間[ sec ]に合わせる
    m_timer->Interval
        = safe_cast< int >( Core::Motion::UnitTime * 1000.0 );
}

/// [ Destructor ]
MotionPanel::~MotionPanel()
{
    if( components != nullptr )
    {
        delete components;
    }
}


#pragma region Windows Form Designer generated code
/// <summary>
/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
/// コード エディタで変更しないでください。
/// </summary>
void MotionPanel::InitializeComponent(void)
{
    this->components = (gcnew System::ComponentModel::Container());
    System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(MotionPanel::typeid));
    this->m_timeLabel = (gcnew System::Windows::Forms::Label());
    this->m_timeTextBox = (gcnew System::Windows::Forms::TextBox());
    this->m_timeTrackBar = (gcnew System::Windows::Forms::TrackBar());
    this->m_unitLabel = (gcnew System::Windows::Forms::Label());
    this->m_appendButton = (gcnew System::Windows::Forms::Button());
    this->m_fastforwardButton = (gcnew System::Windows::Forms::Button());
    this->m_rewindButton = (gcnew System::Windows::Forms::Button());
    this->m_playButton = (gcnew System::Windows::Forms::Button());
    this->m_controlImageList = (gcnew System::Windows::Forms::ImageList(this->components));
    this->m_repeatCheckBox = (gcnew System::Windows::Forms::CheckBox());
    this->m_timer = (gcnew System::Windows::Forms::Timer(this->components));
    (cli::safe_cast<System::ComponentModel::ISupportInitialize^ >(this->m_timeTrackBar))->BeginInit();
    this->SuspendLayout();
    //
    // m_timeLabel
    //
    this->m_timeLabel->AutoSize = true;
    this->m_timeLabel->Location = System::Drawing::Point(5, 42);
    this->m_timeLabel->Name = L"m_timeLabel";
    this->m_timeLabel->Size = System::Drawing::Size(29, 12);
    this->m_timeLabel->TabIndex = 0;
    this->m_timeLabel->Text = L"時間";
    //
    // m_timeTextBox
    //
    this->m_timeTextBox->Location = System::Drawing::Point(40, 39);
    this->m_timeTextBox->Name = L"m_timeTextBox";
    this->m_timeTextBox->Size = System::Drawing::Size(36, 19);
    this->m_timeTextBox->TabIndex = 1;
    this->m_timeTextBox->Text = L"00.0";
    this->m_timeTextBox->TextAlign = System::Windows::Forms::HorizontalAlignment::Right;
    //
    // m_timeTrackBar
    //
    this->m_timeTrackBar->Anchor = static_cast<System::Windows::Forms::AnchorStyles>(((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Left)
        | System::Windows::Forms::AnchorStyles::Right));
    this->m_timeTrackBar->AutoSize = false;
    this->m_timeTrackBar->LargeChange = 10;
    this->m_timeTrackBar->Location = System::Drawing::Point(3, 3);
    this->m_timeTrackBar->Name = L"m_timeTrackBar";
    this->m_timeTrackBar->Size = System::Drawing::Size(425, 30);
    this->m_timeTrackBar->TabIndex = 2;
    this->m_timeTrackBar->TabStop = false;
    this->m_timeTrackBar->ValueChanged += gcnew System::EventHandler(this, &MotionPanel::TimeTrackBarValueChanged);
    //
    // m_unitLabel
    //
    this->m_unitLabel->AutoSize = true;
    this->m_unitLabel->Location = System::Drawing::Point(79, 42);
    this->m_unitLabel->Margin = System::Windows::Forms::Padding(0, 0, 3, 0);
    this->m_unitLabel->Name = L"m_unitLabel";
    this->m_unitLabel->Size = System::Drawing::Size(39, 12);
    this->m_unitLabel->TabIndex = 3;
    this->m_unitLabel->Text = L"( sec )";
    //
    // m_appendButton
    //
    this->m_appendButton->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Right));
    this->m_appendButton->FlatAppearance->BorderSize = 0;
    this->m_appendButton->Location = System::Drawing::Point(427, 7);
    this->m_appendButton->Name = L"m_appendButton";
    this->m_appendButton->Size = System::Drawing::Size(70, 24);
    this->m_appendButton->TabIndex = 4;
    this->m_appendButton->Text = L"追加(&A)";
    this->m_appendButton->UseVisualStyleBackColor = true;
    this->m_appendButton->Click += gcnew System::EventHandler(this, &MotionPanel::AppendButtonClick);
    //
    // m_fastforwardButton
    //
    this->m_fastforwardButton->FlatAppearance->BorderSize = 0;
    this->m_fastforwardButton->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
    this->m_fastforwardButton->Image = (cli::safe_cast<System::Drawing::Image^ >(resources->GetObject(L"m_fastforwardButton.Image")));
    this->m_fastforwardButton->Location = System::Drawing::Point(231, 33);
    this->m_fastforwardButton->Name = L"m_fastforwardButton";
    this->m_fastforwardButton->Size = System::Drawing::Size(45, 30);
    this->m_fastforwardButton->TabIndex = 5;
    this->m_fastforwardButton->UseVisualStyleBackColor = true;
    this->m_fastforwardButton->Click += gcnew System::EventHandler(this, &MotionPanel::FastforwardButtonClick);
    //
    // m_rewindButton
    //
    this->m_rewindButton->FlatAppearance->BorderSize = 0;
    this->m_rewindButton->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
    this->m_rewindButton->Image = (cli::safe_cast<System::Drawing::Image^ >(resources->GetObject(L"m_rewindButton.Image")));
    this->m_rewindButton->Location = System::Drawing::Point(180, 33);
    this->m_rewindButton->Name = L"m_rewindButton";
    this->m_rewindButton->Size = System::Drawing::Size(45, 30);
    this->m_rewindButton->TabIndex = 6;
    this->m_rewindButton->UseVisualStyleBackColor = true;
    this->m_rewindButton->Click += gcnew System::EventHandler(this, &MotionPanel::RewindButtonClick);
    //
    // m_playButton
    //
    this->m_playButton->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((System::Windows::Forms::AnchorStyles::Bottom | System::Windows::Forms::AnchorStyles::Right));
    this->m_playButton->FlatAppearance->BorderSize = 0;
    this->m_playButton->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
    this->m_playButton->ImageIndex = 0;
    this->m_playButton->ImageList = this->m_controlImageList;
    this->m_playButton->Location = System::Drawing::Point(427, 37);
    this->m_playButton->Name = L"m_playButton";
    this->m_playButton->Size = System::Drawing::Size(45, 30);
    this->m_playButton->TabIndex = 7;
    this->m_playButton->UseVisualStyleBackColor = true;
    this->m_playButton->Click += gcnew System::EventHandler(this, &MotionPanel::PlayButtonClick);
    //
    // m_controlImageList
    //
    this->m_controlImageList->ImageStream = (cli::safe_cast<System::Windows::Forms::ImageListStreamer^ >(resources->GetObject(L"m_controlImageList.ImageStream")));
    this->m_controlImageList->TransparentColor = System::Drawing::Color::Transparent;
    this->m_controlImageList->Images->SetKeyName(0, L"control_play_blue.png");
    this->m_controlImageList->Images->SetKeyName(1, L"control_stop_blue.png");
    //
    // m_repeatCheckBox
    //
    this->m_repeatCheckBox->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((System::Windows::Forms::AnchorStyles::Bottom | System::Windows::Forms::AnchorStyles::Right));
    this->m_repeatCheckBox->Appearance = System::Windows::Forms::Appearance::Button;
    this->m_repeatCheckBox->AutoSize = true;
    this->m_repeatCheckBox->FlatAppearance->BorderSize = 0;
    this->m_repeatCheckBox->FlatStyle = System::Windows::Forms::FlatStyle::Flat;
    this->m_repeatCheckBox->Image = (cli::safe_cast<System::Drawing::Image^ >(resources->GetObject(L"m_repeatCheckBox.Image")));
    this->m_repeatCheckBox->Location = System::Drawing::Point(475, 40);
    this->m_repeatCheckBox->Name = L"m_repeatCheckBox";
    this->m_repeatCheckBox->Size = System::Drawing::Size(22, 22);
    this->m_repeatCheckBox->TabIndex = 8;
    this->m_repeatCheckBox->UseVisualStyleBackColor = true;
    //
    // m_timer
    //
    this->m_timer->Tick += gcnew System::EventHandler(this, &MotionPanel::TimerTick);
    //
    // MotionPanel
    //
    this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
    this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    this->Controls->Add(this->m_repeatCheckBox);
    this->Controls->Add(this->m_playButton);
    this->Controls->Add(this->m_rewindButton);
    this->Controls->Add(this->m_fastforwardButton);
    this->Controls->Add(this->m_appendButton);
    this->Controls->Add(this->m_unitLabel);
    this->Controls->Add(this->m_timeTrackBar);
    this->Controls->Add(this->m_timeTextBox);
    this->Controls->Add(this->m_timeLabel);
    this->Name = L"MotionPanel";
    this->Size = System::Drawing::Size(500, 70);
    (cli::safe_cast<System::ComponentModel::ISupportInitialize^ >(this->m_timeTrackBar))->EndInit();
    this->ResumeLayout(false);
    this->PerformLayout();

}
#pragma endregion


/// 初期化する
void MotionPanel::Initialize( Model^ model )
{
    // モーションを生成する
    m_motion = gcnew Core::Motion( model );

    // ... 初期のポーズを追加する
    AppendButtonClick( this, nullptr );
}


/// 時間トラックバーの値が変更された
System::Void MotionPanel::TimeTrackBarValueChanged( System::Object^, System::EventArgs^ )
{
    m_motion->CurrentPoseIndex = m_timeTrackBar->Value;             // 現在のポーズのインデックス
    m_timeTextBox->Text = m_motion->CurrentTime.ToString( "0.0" );  // 時間テキスト


    // 現在の時間が最初でなければ 巻戻しボタンを有効にする
    m_rewindButton->Enabled = ( m_timeTrackBar->Value != m_timeTrackBar->Minimum );

    // 現在の時間が最後でなければ 早送りボタンを有効にする
    m_fastforwardButton->Enabled = ( m_timeTrackBar->Value != m_timeTrackBar->Maximum );
}


/// 追加ボタンがクリックされた
System::Void MotionPanel::AppendButtonClick( System::Object^, System::EventArgs^ )
{
    Debug::WriteLine( "ポーズを追加する" );
    Debug::Assert( m_motion->PoseCount != 0, "ポーズは存在する" );


    const double AdditionalTime = 1.0;    // 追加時間[ sec ]


    // 最後のポーズを取得する
    Pose^ lastPose = m_motion[ m_motion->PoseCount - 1 ];

    // ... 新しい最後のポーズを生成する
    Pose^ newLastPose = gcnew Pose( lastPose->Time + AdditionalTime, lastPose );

    // ... ポーズに追加する
    m_motion->AddToTypicalPose( newLastPose );


    // 時間を 最後のポーズの時間に合わせる
    SetTimeByLastPoseTime();
}


/// 巻戻しボタンがクリックされた
System::Void MotionPanel::RewindButtonClick( System::Object^, System::EventArgs^ )
{
    // 直前の特徴ポーズを取得する
    Pose^ typicalPose = m_motion->GetTypicalPoseJustBefore( m_motion->CurrentPose );

    // ... 時間トラックバーの値を それのインデックスに設定する
    m_timeTrackBar->Value = m_motion->GetPoseIndex( typicalPose );
}

/// 早送りボタンがクリックされた
System::Void MotionPanel::FastforwardButtonClick( System::Object^, System::EventArgs^ )
{
    // 直後の特徴ポーズを取得する
    Pose^ typicalPose = m_motion->GetTypicalPoseJustAfter( m_motion->CurrentPose );

    // ... 時間トラックバーの値を それのインデックスに設定する
    m_timeTrackBar->Value = m_motion->GetPoseIndex( typicalPose );
}


/// 再生ボタンがクリックされた
System::Void MotionPanel::PlayButtonClick( System::Object^, System::EventArgs^ )
{
    if( !m_timer->Enabled )
    {
        // タイマが停止しているならば、時間を最初に設定する
        m_timeTrackBar->Value = 0;


        // タイマを起動する
        m_timer->Start();

        // ... 再生ボタンのイメージを変更する
        m_playButton->ImageIndex = 1;
    }
    else
    {
        // タイマを停止する
        StopTimer( this, nullptr );
    }
}

/// タイマの時間が経過した
System::Void MotionPanel::TimerTick( System::Object^, System::EventArgs^ )
{
    // 時間トラックバーの値を進める
    m_timeTrackBar->Value++;

    /// @note
    /// トラックバーの単位とタイマのインターバルを合わせてあるので、
    /// インクリメントするだけで良い。


    // 時間が最後に到達した
    if( m_timeTrackBar->Value == m_timeTrackBar->Maximum )
    {
        if( m_repeatCheckBox->Checked )
        {
            // リピートが設定されているならば、時間を最初に戻す
            m_timeTrackBar->Value = 0;
        }
        else
        {
            // タイマを停止する
            StopTimer( this, nullptr );
        }
    }
}

/// タイマを停止する
void MotionPanel::StopTimer( System::Object^, System::EventArgs^ )
{
    if( m_timer->Enabled )
    {
        // タイマが起動しているならば、それ停止する
        m_timer->Stop();

        // 再生ボタンのイメージを元に戻す
        m_playButton->ImageIndex = 0;
    }


    /// @todo
    /// モデルに変更を加えられたときに、このハンドラを呼び出して
    /// タイマを停止させる必要がある。
    /// (モデルの状態変化イベントは モーション再生中にも発生するので使えない)
}


/// 時間を 最後のポーズの時間に合わせる
void MotionPanel::SetTimeByLastPoseTime()
{
    int lastPoseIndex = m_motion->PoseCount - 1;// 最後のポーズのインデックス

    m_timeTrackBar->Maximum = lastPoseIndex;    // 時間トラックバーの最大値
    m_timeTrackBar->Value = lastPoseIndex;      // 時間トラックバーの値


    // 時間トラックバーの値 変更イベントを呼び出す
    TimeTrackBarValueChanged( this, nullptr );

    /// @note
    /// 最大値の変更によって、トラックバーの値が変更された場合、
    /// トラックバーの値 変更イベントが発生しない。
    /// よって、明示的にイベントを呼び出している。
}


/// 設定を書き込む
System::Xml::XmlElement^ MotionPanel::SaveSettings( System::Xml::XmlDocument^ document )
{
    if( m_motion->Name == String::Empty )
    {
        // モーション名が設定されていなければ、それを設定する
        if( !SetMotionName() )
        {
            // 設定されなければ、処理を中断する
            return nullptr;
        }
    }

    // モーションに、設定を書き込ませる
    return m_motion->SaveSettings( document );
}

/// 設定を読み込む
void MotionPanel::LoadSettings( System::Xml::XmlDocument^ document )
{
    // タイマを停止する
    StopTimer( this, nullptr );


    // モーションに、設定を読み込ませる
    m_motion->LoadSettings( document );

    // 時間を 最後のポーズの時間に合わせる
    SetTimeByLastPoseTime();
}


/// モーション名を取得する [ Property ] [ Delegate ]
System::String^ MotionPanel::MotionName::get()
{
    return m_motion->Name;
}


/// モーション名を設定する
bool MotionPanel::SetMotionName()
{
    // テキスト入力ダイアログを表示する
    TextInputDialog^ dialog = gcnew TextInputDialog( "モーション名" );

    // ... 空のテキストを禁止する
    dialog->ProhibitEmptyText();


    // ダイアログを表示する
    if( dialog->ShowDialog() == Windows::Forms::DialogResult::OK )
    {
        /// @todo
        /// 同名のモーションが存在するならば、上書きの確認をする

        m_motion->Name = dialog->Value; // モーションの名称
        return true;
    }
    return false;
}