/*
 * Form1Util.cs
 * Copyright (c) 2008-2009 kbinani
 *
 * This file is part of LipSync.
 *
 * LipSync is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * LipSync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.Windows.Forms;

using Boare.Lib.AppUtil;
using Boare.Lib.Vsq;

namespace LipSync {

    public partial class Form1 {
        public void ApplyFont( Font font ) {
            this.Font = font;
            foreach ( Control c in this.Controls ) {
                Boare.Lib.AppUtil.Misc.ApplyFontRecurse( c, font );
            }
        }

        private void ChangeAviWriting( bool value ) {
            AviWriting = value;
        }

        private void ResizePanel2() {
            if ( m_initialized ) {
                side.Height = m_panels.Panel2.Height;
                pictureBox1.Width = m_panels.Panel2.Width - side.Width - vScrollBar1.Width;
                pictureBox1.Height = m_panels.Panel2.Height - hScrollBar1.Height;

                vScrollBar1.Left = pictureBox1.Left + pictureBox1.Width;
                vScrollBar1.Height = pictureBox1.Height;

                hScrollBar1.Top = m_panels.Panel2.Height - hScrollBar1.Height;
                hScrollBar1.Width = pictureBox1.Width;

                pictureBox2.Top = pictureBox1.Top + pictureBox1.Height;
                pictureBox2.Left = vScrollBar1.Left;
                this.Invalidate();
                SetHScrollRange();
                SetVScrollRange();
                m_startToDrawY = StartToDrawY();
            }
        }

        private float GetSnapPoint( float time ) {
            if ( m_grids != null ) {
                if ( m_grids.Length > 0 ) {
                    float nearest = m_grids[0];
                    float dif = Math.Abs( m_grids[0] - time );
                    for ( int i = 1; i < m_grids.Length; i++ ) {
                        float d = Math.Abs( m_grids[i] - time );
                        if ( d < dif ) {
                            dif = d;
                            nearest = m_grids[i];
                        }
                    }
                    return nearest;
                }
            }
            return time;
        }

        private void PreviewWindowFlipMode() {
            if ( previewer.Parent == m_form_preview ) {
                previewer.Parent = m_panels.Panel1;
                m_form_preview.Hide();
                int total_height = m_panels.Panel1.Height + m_panels.Panel2.Height;
                int new_distance = (int)(total_height * AppManager.Config.LastPreviewAspecto);
                m_panels.SplitterDistance = new_distance;
                m_panels.IsSplitterFixed = false;
                menuVisualPreviewSeparate.Checked = false;
            } else {
                AppManager.Config.LastPreviewAspecto = ((float)m_panels.Panel1.Height) / ((float)(m_panels.Panel2.Height + m_panels.Panel1.Height));
                m_panels.SplitterDistance = 0;
                m_panels.IsSplitterFixed = true;
                previewer.Parent = m_form_preview;
                menuVisualPreviewSeparate.Checked = true;
                m_form_preview.Show();
            }
        }

        /// <summary>
        /// 表示言語の設定を適用します。
        /// </summary>
        public void ApplyLanguage() {
            this.xmenuFile.Text = _( "File" ) + "(&F)";
            this.xmenuFileOpen.Text = _( "Open" ) + "(&O)";
            this.xmenuFileSave.Text = _( "Save" ) + "(&S)";
            this.xmenuFileSaveAs.Text = _( "Save As" ) + "(&A)";
            this.xmenuFileImport.Text = _( "Import" ) + "(&I)";
            this.xmenuFileImportRipsync.Text = _( "from RipSync data" );
            this.xmenuFileOpenVsq.Text = _( "Open VSQ/UST file" ) + "(&V)";
            this.xmenuFileExit.Text = _( "Exit" ) + "(&X)";
            this.xmenuFileExportVocaloMark.Text = _( "Script for VOCALOMARK" );
            //this.menuWindow.Text = MessageEx.GetMessage( MessageID.WINDOW ) + "(&W)";
            //this.menuPreview.Text = MessageEx.GetMessage( MessageID.SHOW_PREVIEW ) + "(&P)";
            //this.menuMove.Text = MessageEx.GetMessage( MessageID.GOTO_PREVIEW ) + "(&M)";
            this.menuHelp.Text = _( "Help" ) + "(&H)";
            this.menuHelpPluginInfo.Text = _( "Plugin Information" ) + "(&P)";
            this.menuHelpVersionInfo.Text = _( "About LipSync" ) + "(&A)";
            this.menuHelpDebug.Text = _( "Debug" );
            this.menuHelpDebugEditCharacter.Text = _( "Edit Character" );
            this.menuHelpDebugInverseImages.Text = _( "Flip Images Horizontaly" );
            this.menuEdit.Text = _( "Edit" ) + "(&E)";
            this.menuEditUndo.Text = _( "Undo" ) + "(&U)";
            this.menuEditRedo.Text = _( "Redo" ) + "(&R)";
            this.menuEditVideoSize.Text = _( "Video Size" ) + "(&V)";
            this.menuEditFrameRate.Text = _( "Frame Rate" ) + "(&F)";
            this.menuEditZOrder.Text = _( "Z order" ) + "(&Z)";
            this.menuEditVideoLength.Text = _( "Video Length" ) + "(&C)";
            this.menuEditShiftTimeline.Text = _( "Shift All Time-tables" ) + "(&S)";
            this.menuTool.Text = _( "Tool" ) + "(&T)";
            this.menuToolOption.Text = _( "Option" ) + "(&O)";
            this.menuToolPluginConfig.Text = _( "Plugin Config" ) + "(&P)";
            this.menuEditAddCharacter.Text = _( "Add Empty Character" ) + "(&A)";

            this.xmenuFileOutputAvi.Text = _( "Generate Avi" ) + "(&G)";
            this.menuToolMusic.Text = _( "Chose Sound File" ) + "(&M)";
            this.menuToolMoveTo.Text = _( "Go to Specified Frame" ) + "(&G)";
            this.xmenuFileOutputAbort.Text = _( "Stop Writing Avi" ) + "(&E)";
            this.menuEditBGColor.Text = _( "Background Color" ) + "(&B)";
            this.xmenuFileClose.Text = _( "Close" ) + "(&C)";
            this.xmenuFileOutputRawAvi.Text = _( "Generate Raw Avi" ) + "(&R)";
            this.xmenuFileExport.Text = _( "Export" );
            this.xmenuFileExportHatwune.Text = _( "Script for H@TWUNEBENCH" );
            if ( AppManager.Config.PropertyHidden ) {
                this.menuVisualObjectList.Text = _( "Show Object List" );
            } else {
                this.menuVisualObjectList.Text = _( "Hide Object List" );
            }
            this.menuHelpBugReport.Text = _( "Bug Report" ) + "(&B)";

            string draft_filter = _( "VOCALOID2 Sequence File(*.vsq)|*.vsq" ) + "|" +
                                  _( "All Files(*.*)|*.*" );
            if ( Common.CheckFilterValidity( draft_filter ) ){
                this.openVsqDialog.Filter = draft_filter;
            } else {
                this.openVsqDialog.Filter = "VOCALOID2 Sequence File(*.vsq)|*.vsq|All Files(*.*)|*.*";
            }

            draft_filter = _( "Image Files(*.bmp,*.png,*.jpg,*.jpeg)|*.bmp;*.png;*.jpg;*.jpeg" ) + "|" +
                           _( "Avi File(*.avi)|*.avi" ) + "|" +
                           _( "All Files(*.*)|*.*" );
            if ( Common.CheckFilterValidity( draft_filter ) ){
                this.dialogImage.Filter = draft_filter;
            } else {
                this.dialogImage.Filter =  "Image Files(*.bmp,*.png,*.jpg,*.jpeg)|*.bmp;*.png;*.jpg;*.jpeg|Avi File(*.avi)|*.avi|All Files(*.*)|*.*";
            }

            draft_filter = _( "LipSync Data File(*.lse)|*.lse" ) + "|" +
                           _( "All Files(*.*)|*.*" );
            if ( Common.CheckFilterValidity( draft_filter ) ){
                this.openLse.Filter = draft_filter;
            } else {
                this.openLse.Filter = "LipSync Data File(*.lse)|*.lse|All Files(*.*)|*.*";
            }

            draft_filter = _( "LipSync Data file(*.lse)|*.lse" ) + "|" +
                           _( "All Files(*.*)|*.*" );
            if ( Common.CheckFilterValidity( draft_filter ) ){
                this.saveFileDialog1.Filter = draft_filter;
            } else {
                this.saveFileDialog1.Filter = "LipSync Data file(*.lse)|*.lse|All Files(*.*)|*.*";
            }
            //this.menuGenTelop.Text = MessageEx.GetMessage( MessageID.GENERATE_TELOP_FROM_VSQ ) + "(&T)";
            menuEditRealTime.Text = _( "Real Time \"lipsync\"" );
            cmenuRepeatEnd.Text = _( "End Repeat Here" );
            cmenuRepeatStart.Text = _( "Start Repeat Here" );
            cmenuRepeatReset.Text = _( "Reset Repeat region" );

            #region menuVisual*
            menuVisual.Text = _( "View" ) + "(&V)";
            menuVisualZoomIn.Text = _( "Zoom" ) + "(&I)";
            menuVisualZoomOut.Text = _( "Mooz" ) + "(&O)";
            menuVisualZoomReset.Text = _( "Set Default Scale" ) + "(&R)";
            menuVisualTop.Text = _( "Move to Top" );
            menuVisualEnd.Text = _( "Move to End" );
            menuVisualRepeat.Text = _( "Repeat play" );
            menuVisualRepeatEnd.Text = _( "Move to End" );
            menuVisualRepeatTop.Text = _( "Move to Top" );
            menuVisualTransform.Text = _( "Edit Motion Curve" );
            menuVisualRepeatPlayPause.Text = _( "Play" ) + "/" + _( "Pause" );
            menuVisualVsqTrack.Text = _( "Show VSQ Tracks Allways" );
            menuVisualBars.Text = _( "Show Bars" );
            menuVisualPreview.Text = _( "Preview" ) + "(&P)";
            menuVisualPlayPause.Text = _( "Play" ) + "/" + _( "Pause" );
            menuVisualSync.Text = _( "Sync with Time-table" );
            if ( AppManager.Config.PreviewHidden ) {
                menuVisualPreviewFlipVisible.Text = _( "Enable Preview" );
            } else {
                menuVisualPreviewFlipVisible.Text = _( "Disable Preview" );
            }
            menuVisualPreviewSeparate.Text = _( "Separate Preview Window" ) + "(&S)";
            #endregion

            menuToolQuantize.Text = _( "Quantize" ) + "(&Q)";
            menuToolQuantizeOff.Text = _( "Off" );
            menuToolQuantizeTriplet.Text = _( "Triplet" );

            property.titleUpper.Text = _( "List of Object" );
            property.titleLower.Text = _( "Property" );
            property.ApplyLanguage();

            if ( AppManager.SaveData.m_plugins != null ) {
                foreach ( PluginInfo plugin in AppManager.SaveData.m_plugins ) {
                    plugin.Instance.ApplyLanguage( AppManager.Config.Language );
                }
            }

            if ( m_version_form != null ) {
                m_version_form.ApplyLanguage();
            }
            xmenuFileSeriesBitmap.Text = _( "Series Image" ) + "(&B)";

            previewer.ApplyLanguage();

            if ( m_curve != null ) {
                m_curve.ApplyLanguage();
            }
            if ( m_version_form != null ) {
                m_version_form.ApplyLanguage();
            }
            if ( m_form_preview != null ) {
                m_form_preview.ApplyLanguage();
            }
        }

        private void ReadVsq( string filename ) {
            if ( Path.GetExtension( filename ).ToLower() == ".ust" ) {
                UstFile ust = new UstFile( filename );
                if ( MessageBox.Show( _( "This operation will overwrite all exisiting tempo and time-signal information, and can NOT undo. Would you like to continue?" ),
                                      _( "Confirmation" ), MessageBoxButtons.YesNo,
                                      MessageBoxIcon.Exclamation ) == DialogResult.Yes ) {
                    if ( AppManager.SaveData.m_timesig_ex != null ) {
                        AppManager.SaveData.m_timesig_ex.Clear();
                        AppManager.SaveData.m_timesig_ex = null;
                    }
                    if ( AppManager.SaveData.m_tempo != null ) {
                        AppManager.SaveData.m_tempo.Clear();
                        AppManager.SaveData.m_tempo = null;
                    }
                    AppManager.SaveData.m_timesig_ex = new List<TimeSigTableEntry>();
                    AppManager.SaveData.m_timesig_ex.Add( new TimeSigTableEntry( 0, 4, 4, 0 ) );
                    AppManager.SaveData.m_tempo = new List<TempoTableEntry>( ust.getTempoList().ToArray() );
                    AppManager.SaveData.m_base_tempo = ust.getBaseTempo();
                }
                for ( int i = 0; i < ust.getTrackCount(); i++ ) {
                    AddTimeLineFromUst( ust, i, true );
                }
                AppManager.SaveData.m_totalSec = Math.Max( AppManager.SaveData.m_totalSec, (float)ust.getTotalSec() );
            } else {
                VsqFile vsqFile = new VsqFile( filename );
                int tracks = vsqFile.Track.Count;
                string[] track_names = new string[tracks];
                for ( int track = 0; track < tracks; track++ ) {
                    track_names[track] = vsqFile.Track[track].Name;
                }
                using ( TrackSelecter selecter = new TrackSelecter( filename, track_names ) ) {
                    selecter.ImportTempoAndTimesig = false;
                    if ( selecter.ShowDialog() == DialogResult.OK ) {
                        foreach ( int i in selecter.CheckedItem ) {
                            addTimeLineFromTrack( vsqFile, i, true );
                        }
                        if ( selecter.ImportTempoAndTimesig ) {
                            if ( MessageBox.Show( _( "This operation will overwrite all exisiting tempo and time-signal information, and can NOT undo. Would you like to continue?" ),
                                                  _( "Confirmation" ), MessageBoxButtons.YesNo,
                                                  MessageBoxIcon.Exclamation ) == DialogResult.Yes ) {
                                if ( AppManager.SaveData.m_timesig_ex != null ) {
                                    AppManager.SaveData.m_timesig_ex.Clear();
                                    AppManager.SaveData.m_timesig_ex = null;
                                }
                                if ( AppManager.SaveData.m_tempo != null ) {
                                    AppManager.SaveData.m_tempo.Clear();
                                    AppManager.SaveData.m_tempo = null;
                                }
                                AppManager.SaveData.m_timesig_ex = new List<TimeSigTableEntry>( vsqFile.TimesigTable.ToArray() );
                                AppManager.SaveData.m_tempo = new List<TempoTableEntry>( vsqFile.TempoTable.ToArray() );
                                AppManager.SaveData.m_base_tempo = vsqFile.getBaseTempo();
                            }
                        }
                    }
                }

                AppManager.SaveData.m_totalSec = Math.Max( AppManager.SaveData.m_totalSec, (float)vsqFile.getTotalSec() );

            }
            SetHScrollRange();
            SetVScrollRange();
            UpdateGridList();

            AppManager.Edited = true;
            this.Invalidate();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rnd"></param>
        /// <param name="base_interval"></param>
        /// <param name="randomize"></param>
        /// <returns></returns>
        private float nextInterval( ref Random rnd, float base_interval, bool randomize ) {
            const float BAND_WIDTH = 0.8f;
            if ( randomize ) {
                return base_interval - (BAND_WIDTH / 2f) * base_interval +
                    (float)(rnd.NextDouble()) * base_interval * BAND_WIDTH;
            } else {
                return base_interval;
            }
        }

        /// <summary>
        /// ツールメニューのクオンタイズのチェック状態を，m_quantize_*にあわせて更新します
        /// </summary>
        private void QuantizeMenuCheckedStateUpdate() {
            menuToolQuantize04.Checked = false;
            menuToolQuantize08.Checked = false;
            menuToolQuantize16.Checked = false;
            menuToolQuantize32.Checked = false;
            menuToolQuantize64.Checked = false;
            menuToolQuantizeOff.Checked = false;
            switch ( AppManager.Config.QuantizeMode ) {
                case QuantizeMode.q04:
                    menuToolQuantize04.Checked = true;
                    break;
                case QuantizeMode.q08:
                    menuToolQuantize08.Checked = true;
                    break;
                case QuantizeMode.q16:
                    menuToolQuantize16.Checked = true;
                    break;
                case QuantizeMode.q32:
                    menuToolQuantize32.Checked = true;
                    break;
                case QuantizeMode.q64:
                    menuToolQuantize64.Checked = true;
                    break;
                case QuantizeMode.off:
                    menuToolQuantizeOff.Checked = true;
                    break;
            }
            menuToolQuantizeTriplet.Checked = AppManager.Config.QuantizeTripletEnabled;
            UpdateGridList();
            pictureBox1.Refresh();
        }

        private void ChangeSpeed( int speed ) {
            if ( AppManager.Playing ) {
                m_player.Pause();
            }
            float current = m_player.GetPosition() * 0.001f;
            m_player.Speed = speed * 0.001f;
            m_started_date = DateTime.Now;
            m_started_date = m_started_date.AddSeconds( -current / (0.001f * speed) );
            if ( AppManager.Playing ) {
                m_player.PlayFrom( current );
            }
            float t_speed = speed * 0.001f;
            previewer.LabelSpeedText = "x" + t_speed.ToString( "0.00" );
        }

        public bool AviWriting {
            get {
                return m_avi_writing;
            }
            set {
                m_avi_writing = value;
                xmenuFileOutputAvi.Enabled = !value;
                previewer.PlayPauseEnabled = !value;
                previewer.TrackBarEnabled = !value;
                menuEdit.Enabled = !value;
                xmenuFileOpen.Enabled = !value;
                xmenuFileImportRipsync.Enabled = !value;
                xmenuFileOpenVsq.Enabled = !value;
                pictureBox1.Enabled = !value;
                xmenuFileClose.Enabled = !value;
                xmenuFileSeriesBitmap.Enabled = !value;
                xmenuFileOutputAbort.Enabled = value;
                if ( m_avi_writing ) {
                    m_player.Close();
                } else {
                    if ( AppManager.SaveData.m_audioFile != "" ) {
                        m_player.Load( AppManager.SaveData.m_audioFile );
                    }
                }
            }
        }

        private void ChangePropertyHidden() {
            float total_width = m_container.Panel1.Width + m_container.Panel2.Width;
            if ( AppManager.Config.PropertyHidden ) {
                m_container.SplitterDistance = AppManager.Config.LastPropertyWidth;
                AppManager.Config.PropertyHidden = false;
                menuVisualObjectList.Text = _( "Hide object list" );
                m_container.IsSplitterFixed = false;
            } else {
                AppManager.Config.LastPropertyWidth = m_container.SplitterDistance;
                m_container.SplitterDistance = 0;
                AppManager.Config.PropertyHidden = true;
                menuVisualObjectList.Text = _( "Show object list" );
                m_container.IsSplitterFixed = true;
            }
        }

        /// <summary>
        /// ビットレートを変更したときに呼び出される
        /// </summary>
        private void ChangeBitrate() {
            previewer.TrackBarMaximum = (int)(AppManager.SaveData.m_totalSec * AppManager.SaveData.FrameRate);
        }

        private void UpdateFormTitle() {
            string changed_title;
            if ( menuEditRealTime.Checked ) {
                changed_title = "[REAL TIME] ";
            } else {
                changed_title = "";
            }
            if ( AppManager.Edited ) {
                changed_title += "* ";
            } else {
                changed_title += "";
            }
            if ( m_filePath == "" ) {
                changed_title += _( "(no name)" );
            } else {
                changed_title += Path.GetFileName( m_filePath );
            }
            changed_title += " - LipSync";
            if ( this.Text != changed_title ) {
                this.Text = changed_title;
            }
        }

        /// <summary>
        /// プレビューの非表示・表示を切り替えます
        /// </summary>
        private void ChangePreviewHidden() {
            if ( AppManager.Config.PreviewHidden ) {
                int total_height = m_panels.Panel1.Height + m_panels.Panel2.Height;
                int new_distance = (int)(total_height * AppManager.Config.LastPreviewAspecto);
                m_panels.SplitterDistance = new_distance;
                AppManager.Config.PreviewHidden = false;
                menuVisualPreviewFlipVisible.Text = _( "Disable Preview" );
                m_panels.IsSplitterFixed = false;
            } else {
                AppManager.Config.LastPreviewAspecto = ((float)m_panels.Panel1.Height) / ((float)(m_panels.Panel2.Height + m_panels.Panel1.Height));
                m_panels.SplitterDistance = 0;
                AppManager.Config.PreviewHidden = true;
                menuVisualPreviewFlipVisible.Text = _( "Enable Preview" );
                m_panels.IsSplitterFixed = true;
            }
        }

        private void Save( string file ) {
            bool backup_created = false;
            string new_file = "";
            if ( File.Exists( file ) ) {
                new_file = file + "_";
                while ( File.Exists( new_file ) ) {
                    new_file += "_";
                }
                File.Move( file, new_file );
                backup_created = true;
            }
            using ( SettingsEx temp = (SettingsEx)AppManager.SaveData.Clone() )
            using ( FileStream fs = new FileStream( file, FileMode.Create ) ) {
                BinaryFormatter bf = new BinaryFormatter();
                for ( int i = 0; i < m_not_used_plugin.Count; i++ ) {
                    temp.m_group_plugin.Add( (TimeTable)m_not_used_plugin[i].Clone() );
                }
                for ( int i = 0; i < m_not_used_plugin_config.Count; i++ ) {
                    temp.m_plugins_config.Add( m_not_used_plugin_config[i].Clone() );
                }
                try {
                    bf.Serialize( fs, temp );
                    AppManager.Edited = false;
                } catch ( Exception ex ) {
                    Common.LogPush( ex );
                    MessageBox.Show(
                        _( "Failed file saving" ),
                        _( "Error" ),
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation );
                    fs.Close();
                    if ( backup_created ) {
                        try {
                            File.Delete( file );
                            File.Move( new_file, file );
                        } catch {
                        }
                    }
                } finally {
                    bf = null;
                    if ( backup_created && File.Exists( new_file ) ) {
                        File.Delete( new_file );
                    }
                }
            }
        }

        private void Read( string file ) {
#if DEBUG
            Common.DebugWriteLine( "read(string)" );
#endif
            StopPauseCore(); //timerPreview.Enabled = false;

            if ( AppManager.Edited ) {
                DialogResult result = requestIntention();
                if ( result == DialogResult.Yes ) {
                    if ( m_filePath != "" ) {
                        Save( m_filePath );
                    } else {
                        if ( saveFileDialog1.ShowDialog() == DialogResult.OK ) {
                            Save( saveFileDialog1.FileName );
                        } else {
                            return;
                        }
                    }
                } else if ( result == DialogResult.Cancel ) {
                    return;
                }
            }

            previewer.TrackBarValue = previewer.TrackBarMinimum;
            using ( FileStream fs = new FileStream( file, FileMode.Open ) ) {
                BinaryFormatter bf = new BinaryFormatter();
                m_skip_paint = true;

                List<PluginInfo> t_plugin_info = new List<PluginInfo>();
                foreach ( PluginInfo pinfo in AppManager.SaveData.m_plugins ) {
                    t_plugin_info.Add( (PluginInfo)pinfo.Clone() );
                }

                object tmp = bf.Deserialize( fs );
                if ( tmp.GetType() == typeof( LipSync.Settings ) ) {
#if DEBUG
                    Common.DebugWriteLine( "    deserialized object is LipSync.Settings" );
#endif
                    Settings tSettings = (Settings)tmp;
                    AppManager.SaveData.Dispose();
                    AppManager.SaveData = new SettingsEx( tSettings );
                } else if ( tmp.GetType() == typeof( LipSync.SettingsEx ) ) {
#if DEBUG
                    Common.DebugWriteLine( "    deserialized object is LipSync.SettingsEx" );
#endif
                    AppManager.SaveData.Dispose();
                    AppManager.SaveData = (SettingsEx)tmp;
                } else {
                    // 読み込み失敗
                    MessageBox.Show(
                        _( "Failed file reading" ),
                        _( "Error" ),
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error );
                    return;
                }

                AppManager.SaveData.m_zorder.Clear();

                int n_plugin = t_plugin_info.Count;
                if ( n_plugin > 0 ) {
                    AppManager.SaveData.m_plugins = new PluginInfo[t_plugin_info.Count];
                    for ( int i = 0; i < t_plugin_info.Count; i++ ) {
                        AppManager.SaveData.m_plugins[i] = (PluginInfo)t_plugin_info[i].Clone();
                    }
                }

                AppManager.Edited = false;

                // いまインストールされているプラグインの情報を元に、
                // 使用できるプラグインの情報のみをm_変数に入れる
                // 使えないものは、m_not_used_変数に保管しておく。
                m_not_used_plugin.Clear();
                m_not_used_plugin_config.Clear();

                List<TimeTable> tmp_ttable = new List<TimeTable>();
                List<PluginConfig> tmp_config = new List<PluginConfig>();
                for ( int i = 0; i < AppManager.SaveData.m_plugins_config.Count; i++ ) {
#if DEBUG
                    Common.DebugWriteLine( "Form1.read(String); m_plugins_config[]; id=" + AppManager.SaveData.m_plugins_config[i].ID + "; config=" + AppManager.SaveData.m_plugins_config[i].Config );
#endif
                    bool found = false;
                    for ( int j = 0; j < AppManager.SaveData.m_plugins.Length; j++ ) {
                        if ( AppManager.SaveData.m_plugins_config[i].ID == AppManager.SaveData.m_plugins[j].ID ) {
                            found = true;
                            break;
                        }
                    }
                    if ( found ) {
                        tmp_ttable.Add( (TimeTable)AppManager.SaveData.m_group_plugin[i].Clone() );
                        tmp_config.Add( AppManager.SaveData.m_plugins_config[i].Clone() );
                    } else {
                        m_not_used_plugin.Add( (TimeTable)AppManager.SaveData.m_group_plugin[i].Clone() );
                        m_not_used_plugin_config.Add( AppManager.SaveData.m_plugins_config[i].Clone() );
                    }
                }

                // Settingsに転送
                AppManager.SaveData.m_group_plugin.Clear();
                AppManager.SaveData.m_plugins_config.Clear();
                for ( int i = 0; i < tmp_ttable.Count; i++ ) {
                    AppManager.SaveData.m_group_plugin.Add( (TimeTable)tmp_ttable[i].Clone() );
                    AppManager.SaveData.m_plugins_config.Add( tmp_config[i].Clone() );
                }

                // プラグインの全体設定を適用
                for ( int i = 0; i < AppManager.SaveData.m_plugins.Length; i++ ) {
                    string id = AppManager.SaveData.m_plugins[i].ID;
                    AppManager.SaveData.m_plugins[i].Instance.Config = "";
                    for ( int j = 0; j < AppManager.SaveData.m_group_plugin.Count; j++ ) {
                        if ( AppManager.SaveData.m_plugins_config[j].ID == id ) {
                            AppManager.SaveData.m_plugins[i].Instance.Config = AppManager.SaveData.m_plugins_config[j].Config;
                            break;
                        }
                    }
                }

                bf = null;
            }
            AppManager.ClearCommandBuffer();
            AppManager.SaveData.UpdateZorder();
            Telop.DecideLane( AppManager.SaveData.m_telop_ex2 );
            m_skip_paint = false;

            if ( AppManager.SaveData.m_audioFile.Length > 0 && File.Exists( AppManager.SaveData.m_audioFile ) ) {
                m_player.Load( AppManager.SaveData.m_audioFile );
            }

            previewer.Image = new Bitmap( AppManager.SaveData.m_movieSize.Width, AppManager.SaveData.m_movieSize.Height );
            using ( Graphics g = Graphics.FromImage( previewer.Image ) ) {
                AppManager.SaveData.DrawTo( g, previewer.Image.Size, Now, false );
            }
            previewer.Invalidate();

            UpdateFormTitle();
            this.Invalidate();
#if DEBUG
            for ( int i = 0; i < AppManager.SaveData.m_plugins_config.Count; i++ ) {
                Common.DebugWriteLine( "Form1.read(String); registered; id=" + AppManager.SaveData.m_plugins_config[i].ID + "; config=" + AppManager.SaveData.m_plugins_config[i].Config );
            }
            for ( int i = 0; i < m_not_used_plugin_config.Count; i++ ) {
                Common.DebugWriteLine( "Form1.read(String); not registered; id=" + m_not_used_plugin_config[i].ID );
            }
            for ( int i = 0; i < AppManager.SaveData.m_plugins.Length; i++ ) {
                Common.DebugWriteLine( "Form1.read(String); current plugin; id=" + AppManager.SaveData.m_plugins[i].ID + "; Config=" + AppManager.SaveData.m_plugins[i].Instance.Config );
            }
#endif
        }

        private void sizeChange() {
            using ( SetSize<int> setsize = new SetSize<int>( _( "Video size configuration" ), _( "Width" ), _( "Height" ), AppManager.SaveData.m_movieSize.Width, AppManager.SaveData.m_movieSize.Height ) ) {
                if ( setsize.ShowDialog() == DialogResult.OK ) {
                    Command run = Command.GCommandChangeVideoSize( new Size( (int)setsize.ResultWidth, (int)setsize.ResultHeight ) );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    previewer.Image = new Bitmap( (int)setsize.ResultWidth, (int)setsize.ResultHeight );
                    using ( Graphics g = Graphics.FromImage( previewer.Image ) ) {
                        AppManager.SaveData.DrawTo( g, previewer.Image.Size, Now, false );
                    }
                    previewer.Invalidate();
                    AppManager.Edited = true;
                }
            }
        }

        public Bitmap GetPicture( float now, bool is_transparent ) {
            if ( m_skip_paint ) {
                return null;
            }
            Size mSize = AppManager.SaveData.m_movieSize;
            Bitmap bmp = is_transparent ?
                new Bitmap( mSize.Width, mSize.Height, PixelFormat.Format32bppArgb ) :
                new Bitmap( mSize.Width, mSize.Height, PixelFormat.Format24bppRgb );
            using ( Graphics g = Graphics.FromImage( bmp ) ) {
                AppManager.SaveData.DrawTo( g, mSize, now, is_transparent );
            }
            return bmp;
        }

        private void DeleteEntry() {
            if ( m_edit_mode == EditMode.Selected/* m_editMode*/ ) {
                bool changed = false;
                TimeTableType type = m_clicked.type;
                int group = m_clicked.group;
                int track = m_clicked.track;
                int entry = m_clicked.entry;
                if ( type == TimeTableType.character || type == TimeTableType.another || type == TimeTableType.plugin ) {
                    Command run = Command.GCommandDeleteTimeTableEntry( type, group, track, AppManager.SaveData[type, group][track][entry] );
                    AppManager.Register( AppManager.SaveData.Execute( run ) );
                    changed = true;
                }
                if ( changed ) {
                    m_edit_mode = EditMode.None;
                    AppManager.Edited = true;
                    pictureBox1.Invalidate();
                }
            }
        }

        private float RepeatStart {
            get {
                return AppManager.SaveData.REPEAT_START;
            }
        }

        private float RepeatEnd {
            get {
                if ( AppManager.SaveData.REPEAT_END < 0 ) {
                    return AppManager.SaveData.m_totalSec;
                } else {
                    return AppManager.SaveData.REPEAT_END;
                }
            }
        }

        private void UpdateExpandRange( float time ) {
            bool exclusion_mode = false;

            if ( m_clicked.type == TimeTableType.character ) {
                string title = AppManager.SaveData.m_groups_character[m_clicked.group][m_clicked.track].Text;
                string tag = AppManager.SaveData.m_groups_character[m_clicked.group].Character[title].tag;
                if ( tag != "" ) {
                    exclusion_mode = true;
                }
            }

            if ( exclusion_mode ) {
                m_expandRange = GetEntryLimit( m_clicked, time, true );
            } else {
                m_expandRange = GetEntryLimitSingle( m_clicked, time, true );
            }
        }

        private void UpdateEditHandle( int x, int y ) {
            Rectangle hilight = GetHilightRect( x, y );
            m_editHandleLeft.X = hilight.X - HANDLE_WIDTH / 2;
            m_editHandleLeft.Y = hilight.Y;
            m_editHandleLeft.Width = HANDLE_WIDTH;
            m_editHandleLeft.Height = AppManager.Config.TrackHeight;

            m_editHandleRight.X = hilight.X + hilight.Width - HANDLE_WIDTH / 2;
            m_editHandleRight.Y = hilight.Y;
            m_editHandleRight.Width = HANDLE_WIDTH;
            m_editHandleRight.Height = AppManager.Config.TrackHeight;
        }

        private void SetHScrollRange() {
            AppManager.SaveData.m_screenWidth = (int)(AppManager.SaveData.m_totalSec * AppManager.Config.PixelPerSec);
            previewer.TrackBarMaximum = (int)(AppManager.SaveData.m_totalSec * AppManager.SaveData.FrameRate);
            if ( AppManager.SaveData.m_totalSec == 0.0f ) {
                hScrollBar1.Maximum = 109;
            } else {
                hScrollBar1.Maximum = (int)(AppManager.SaveData.m_totalSec * AppManager.Config.PixelPerSec);
                hScrollBar1.LargeChange = pictureBox1.Width;
                if ( AppManager.SaveData.m_screenWidth > pictureBox1.Width ) {
                    hScrollBar1.Enabled = true;
                } else {
                    hScrollBar1.Enabled = false;
                }
            }
        }

        private void SetVScrollRange() {
            if ( AppManager.SaveData.m_group_vsq == null ) {
                return;
            }
            int[] lane = AppManager.GetTimeLineLanes();
            int tracks = 1;
            for ( int i = 0; i < lane.Length; i++ ) {
                tracks += lane[i];
            }
            AppManager.SaveData.m_screenHeight = tracks * AppManager.Config.TrackHeight;
            vScrollBar1.Maximum = AppManager.SaveData.m_screenHeight;
            vScrollBar1.LargeChange = pictureBox1.Height;
            if ( AppManager.SaveData.m_screenHeight > pictureBox1.Height ) {
                vScrollBar1.Enabled = true;
            } else {
                vScrollBar1.Value = vScrollBar1.Minimum;
                vScrollBar1.Enabled = false;
            }
        }

        private int XCoordFromSec( float time ) {
            return (int)(time * AppManager.Config.PixelPerSec) - m_startToDrawX;
        }

        /// <summary>
        /// pictureBox1上のx座標位置を、秒数に変換します
        /// </summary>
        /// <param name="pixel"></param>
        /// <returns></returns>
        private float SecFromXCoord( int pixel ) {
            return (pixel + m_startToDrawX) / AppManager.Config.PixelPerSec;
        }

        private int StartToDrawX() {
            float ratio = ((float)(hScrollBar1.Value) / (float)(hScrollBar1.Maximum - hScrollBar1.Minimum + 1 - hScrollBar1.LargeChange));
            if ( AppManager.SaveData.m_screenWidth - pictureBox1.Width >= 0 ) {
                return (int)((AppManager.SaveData.m_screenWidth - pictureBox1.Width) * ratio);
            } else {
                return 0;
            }
        }

        private int StartToDrawY() {
            float ratio = ((float)(vScrollBar1.Value) / ((float)(vScrollBar1.Maximum - vScrollBar1.Minimum + 1 - vScrollBar1.LargeChange)));
            //MessageBox.Show( "StartToDrawY=" + (int)(ratio * (m_screenHeight - pictureBox1.Height)) );
            if ( AppManager.SaveData.m_screenHeight - pictureBox1.Height >= 0 ) {
                return (int)(ratio * (AppManager.SaveData.m_screenHeight - pictureBox1.Height));
            } else {
                return 0;
            }
        }

        /// <summary>
        /// s.m_groups_characterの第group番グループの、時刻timeの位置において、
        /// 延長可能な時刻をPointEx型で返します
        /// </summary>
        /// <param name="group">グループのインデックス</param>
        /// <param name="time">時刻</param>
        /// <returns>
        /// 指定されたグループの他のTimeTableType.mouthと被らずに前後に延長することができる範囲。
        /// </returns>
        private PointF GetEntryLimit( Item item, float time, bool ignore_me ) {
            if ( item.type == TimeTableType.character ) {
                string title = AppManager.SaveData.m_groups_character[item.group][item.track].Text;
                string tag = AppManager.SaveData.m_groups_character[item.group].Character[title].tag;

                int track_num = AppManager.SaveData.m_groups_character[item.group].Count;
                PointF[] points = new PointF[track_num];
                for ( int track = 0; track < track_num; track++ ) {
                    Item tmp = new Item( item.type, item.group, item.track, item.entry, item.row_index );
                    string tmp_title = AppManager.SaveData.m_groups_character[item.group][track].Text;
                    string tmp_tag = AppManager.SaveData.m_groups_character[item.group].Character[tmp_title].tag;
                    if ( tag != "" && tag == tmp_tag ) {
                        tmp.track = track;
                        if ( track == item.track ) {
                            points[track] = GetEntryLimitSingle( tmp, time, ignore_me );
                        } else {
                            points[track] = GetEntryLimitSingle( tmp, time, false );
                        }
                    } else {
                        points[track].X = 0.0f;
                        points[track].Y = AppManager.SaveData.m_totalSec;
                    }
                }
                PointF result = new PointF( 0.0f, AppManager.SaveData.m_totalSec );
                for ( int track = 0; track < track_num; track++ ) {
                    result.X = Math.Max( result.X, points[track].X );
                    result.Y = Math.Min( result.Y, points[track].Y );
                }
                return result;

            } else {
                return new PointF( time, time );
            }
        }

        /// <summary>
        /// Item構造体で指定される、トラックの時刻timeの位置にエントリを追加した場合に、
        /// 同じトラックの前後のエントリと被らずに延長することのできる時間の範囲を調べます。
        /// ignore_me == trueの時、item.entryの存在を無視します。
        /// </summary>
        /// <param name="item"></param>
        /// <param name="time"></param>
        /// <param name="ignore_me"></param>
        /// <returns></returns>
        private PointF GetEntryLimitSingle( Item item, float time, bool ignore_me ) {
            TimeTableType type = item.type;
            int group = item.group;
            int track = item.track;

            if ( type == TimeTableType.character || type == TimeTableType.another || type == TimeTableType.plugin ) {
                return GetEntryLimitSinglePart( AppManager.SaveData[type, group][track], time, ignore_me, item );
            } else {
                return new PointF( time, time );
            }
        }

        private PointF GetEntryLimitSinglePart( TimeTable table, float time, bool ignore_me, Item item ) {
            if ( table.Count == 0 ) {
                return new PointF( 0.0f, AppManager.SaveData.m_totalSec );
            }
            int index_before;
            int index_after;
            index_before = -1;
            index_after = table.Count;
            float begin;
            float end;
            for ( int entry = 0; entry < table.Count; entry++ ) {
                if ( ignore_me && entry == item.entry ) {
                    continue;
                }
                if ( time < table[entry].begin ) {
                    index_after = entry;
                    break;
                }
            }
            for ( int entry = index_after - 1; entry >= 0; entry-- ) {
                if ( ignore_me && entry == item.entry ) {
                    continue;
                }
                if ( table[entry].end < time ) {
                    index_before = entry;
                    break;
                }
            }

            // 新しく設定する時刻をセット
            if ( index_before == -1 ) {
                begin = 0.0f;
            } else {
                begin = table[index_before].end;
            }
            if ( index_after == table.Count ) {
                end = AppManager.SaveData.m_totalSec;
            } else {
                end = table[index_after].begin;
            }

            // 
            if ( index_after == index_before + 1 ) {
                return new PointF( begin, end );
            } else {
                if ( ignore_me && index_before == item.entry - 1 && index_after == item.entry + 1 ) {
                    return new PointF( begin, end );
                } else {
                    return new PointF( time, time );
                }
            }
        }

        /// <summary>
        /// マウスのy座標から、タイムテーブル・グループの番号を取得します
        /// </summary>
        /// <param name="y">仮想スクリーン上のマウスの座標</param>
        /// <returns>
        /// 第iタイムテーブルグループのタイトルにマウスがあったとき
        ///   new Item( i, -1, -1)
        /// 第iタイムテーブル・グループの第jタイムテーブルの、テーブルエントリ以外の場所にマウスがあったとき
        ///   new Item( i, j, -1 )
        /// 第iタイムテーブル・グループの第jタイムテーブルの第kエントリーにマウスがあったとき
        ///   new Item( i, j, k )
        /// </returns>
        private Item GetGroupItem( int x, int y ) {
            int track_count = 0;
            Item item = new Item( TimeTableType.none, -1, -1, -1, -1 );
            if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                item.type = TimeTableType.top;
                return item;
            }

            bool vsq_fixed = menuVisualVsqTrack.Checked;
            /*
             * vsqのグループを検索
             */
            // タイトル
            track_count++;
            int yy = y;
            if ( vsq_fixed ) {
                yy = y - m_startToDrawY;
            }
            if ( track_count * AppManager.Config.TrackHeight <= yy && yy < (track_count + 1) * AppManager.Config.TrackHeight ) {
                item.type = TimeTableType.vsq;
                item.group = 0;
                item.row_index = track_count;
                return item;
            }
            if ( !AppManager.SaveData.m_group_vsq.Folded ) {
                // 本体
                for ( int i = 0; i < AppManager.SaveData.m_group_vsq.Count; i++ ) {
                    track_count++;
                    if ( track_count * AppManager.Config.TrackHeight <= yy && yy < (track_count + 1) * AppManager.Config.TrackHeight ) {
                        item.type = TimeTableType.vsq;
                        item.group = 0;
                        item.track = i;
                        item.row_index = track_count;
                        for ( int entry = 0; entry < AppManager.SaveData.m_group_vsq[item.track].Count; entry++ ) {
                            int xx = (int)(AppManager.SaveData.m_group_vsq[item.track][entry].begin * AppManager.Config.PixelPerSec);
                            int xend = (int)(AppManager.SaveData.m_group_vsq[item.track][entry].end * AppManager.Config.PixelPerSec);
                            if ( xx <= x && x < xend ) {
                                item.entry = entry;
                                break;
                            }
                        }
                        return item;
                    }
                }
            }

            /*
             * キャラクタのグループを検索
             */
            for ( int group = 0; group < AppManager.SaveData.m_groups_character.Count; group++ ) {
                // タイトル
                track_count++;
                if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                    if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height) ) {
                        item.type = TimeTableType.character;
                        item.group = group;
                        item.row_index = track_count;
                        return item;
                    }
                }
                if ( !AppManager.SaveData.m_groups_character[group].Folded ) {
                    for ( int track = 0; track < AppManager.SaveData.m_groups_character[group].Count; track++ ) {
                        track_count++;
                        if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                            if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height) ) {
                                item.type = AppManager.SaveData.m_groups_character[group][track].Type;
                                item.group = group;
                                item.track = track;
                                item.row_index = track_count;
                                for ( int entry = 0; entry < AppManager.SaveData.m_groups_character[item.group][item.track].Count; entry++ ) {
                                    int xx = (int)(AppManager.SaveData.m_groups_character[item.group][item.track][entry].begin * AppManager.Config.PixelPerSec);
                                    int xend = (int)(AppManager.SaveData.m_groups_character[item.group][item.track][entry].end * AppManager.Config.PixelPerSec);
                                    if ( xx <= x && x < xend ) {
                                        item.entry = entry;
                                        break;
                                    }
                                }
                                return item;
                            }
                        }
                    }
                }
            }

            /*
             * 字幕を検索
             */
            track_count++;
            if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ){
                if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height ) ){
                    item.type = TimeTableType.telop;
                    item.group = 0;
                    item.track = -1;
                    item.row_index = track_count;
                    return item;
                }
            }
            if ( !AppManager.SaveData.TelopListFolded ){
                if ( (track_count + 1) * AppManager.Config.TrackHeight <= y && y < (track_count + AppManager.MaxTelopLanes + 1) * AppManager.Config.TrackHeight ) {
                    item.type = TimeTableType.telop;
                    item.group = 0;
                    item.track = 0;
                    item.row_index = track_count;
                    for ( int i = 0; i < AppManager.SaveData.m_telop_ex2.Count; i++ ) {
                        int xx = (int)(AppManager.SaveData.m_telop_ex2[i].Start * AppManager.Config.PixelPerSec);
                        int xend = (int)(AppManager.SaveData.m_telop_ex2[i].End * AppManager.Config.PixelPerSec);
                        int y1 = (track_count + AppManager.SaveData.m_telop_ex2[i].Lane + 1) * AppManager.Config.TrackHeight;
                        if ( xx <= x && x < xend && y1 <= y && y < y1 + AppManager.Config.TrackHeight ) {
                            item.row_index = track_count + AppManager.SaveData.m_telop_ex2[i].Lane + 1;
                            item.entry = AppManager.SaveData.m_telop_ex2[i].ID;
                            break;
                        }
                    }
                    return item;
                }
                track_count += AppManager.MaxTelopLanes;
            }

            /*
             * その他のイメージを検索
             */
            track_count++;
            if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height) ) {
                    item.type = TimeTableType.another;
                    item.group = 0;
                    item.row_index = track_count;
                    return item;
                }
            }
            // 本体
            if ( !AppManager.SaveData.m_group_another.Folded ) {
                for ( int i = 0; i < AppManager.SaveData.m_group_another.Count; i++ ) {
                    track_count++;
                    if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                        if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height) ) {
                            item.type = TimeTableType.another;
                            item.group = 0;
                            item.track = i;
                            item.row_index = track_count;
                            for ( int entry = 0; entry < AppManager.SaveData.m_group_another[item.track].Count; entry++ ) {
                                int xx = (int)(AppManager.SaveData.m_group_another[item.track][entry].begin * AppManager.Config.PixelPerSec);
                                int xend = (int)(AppManager.SaveData.m_group_another[item.track][entry].end * AppManager.Config.PixelPerSec);
                                if ( xx <= x && x < xend ) {
                                    item.entry = entry;
                                    break;
                                }
                            }
                            return item;
                        }
                    }
                }
            }

            // プラグインを検索
            track_count++;
            if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height) ) {
                    item.type = TimeTableType.plugin;
                    item.group = 0;
                    item.row_index = track_count;
                    return item;
                }
            }
            // 本体
            if ( !AppManager.SaveData.m_group_plugin.Folded ) {
                for ( int i = 0; i < AppManager.SaveData.m_group_plugin.Count; i++ ) {
                    track_count++;
                    if ( track_count * AppManager.Config.TrackHeight <= y && y < (track_count + 1) * AppManager.Config.TrackHeight ) {
                        if ( !vsq_fixed || (vsq_fixed && yy >= m_vsq_height) ) {
                            item.type = TimeTableType.plugin;
                            item.group = 0;
                            item.track = i;
                            item.row_index = track_count;
                            for ( int entry = 0; entry < AppManager.SaveData.m_group_plugin[item.track].Count; entry++ ) {
                                int xx = (int)(AppManager.SaveData.m_group_plugin[item.track][entry].begin * AppManager.Config.PixelPerSec);
                                int xend = (int)(AppManager.SaveData.m_group_plugin[item.track][entry].end * AppManager.Config.PixelPerSec);
                                if ( xx <= x && x < xend ) {
                                    item.entry = entry;
                                    break;
                                }
                            }
                            return item;
                        }
                    }
                }
            }
            return item;
        }

        /// <summary>
        /// 指定された画像を指定した位置にプレビュー表示します
        /// </summary>
        /// <param name="image">表示する画像</param>
        /// <param name="position">pictureBox1上のプレビュー画像の表示位置位置</param>
        private void ShowPreviewPictureBox( Image image, Point position ) {
            if ( image == null ) {
                return;
            }
            int width = image.Width;
            int height = image.Height;
            preview_image.Width = width;
            preview_image.Height = height;
            preview_image.Image = image;
            int x = position.X;
            int y = position.Y;
            bool right = false;
            bool down = false;
            if ( pictureBox1.Width / 2 < x ) {
                x -= width;
                right = true;
            }
            if ( pictureBox1.Height / 2 < y ) {
                y -= height;
                down = true;
            }
            float scale_x = 1f;
            if ( x + width > pictureBox1.Width ) {
                scale_x = (float)(pictureBox1.Width - x) / (float)width;
            } else if ( x < 0 ) {
                scale_x = (float)(x + width) / (float)width;
            }
            float scale_y = 1f;
            if ( y + height > pictureBox1.Height ) {
                scale_y = (float)(pictureBox1.Height - y) / (float)height;
            } else if ( y < 0 ) {
                scale_y = (float)(y + height) / (float)height;
            }
            float scale = Math.Min( scale_x, scale_y );
            if ( scale < 1f ) {
                preview_image.Size = new Size( (int)(width * scale), (int)(height * scale) );
                if ( right ) {
                    x += (int)(width - width * scale);
                } else {
                    if ( x < 0 ) {
                        x = 0;
                    }
                }
                if ( down ) {
                    y += (int)(height - height * scale);
                } else {
                    if ( y < 0 ) {
                        y = 0;
                    }
                }
            }
            preview_image.Top = y;//position.Y;// + Panels.Panel2.Top + 1;
            preview_image.Left = x;//position.X;// + Panels.Panel2.Left + 1;

            m_preview_time = DateTime.Now;
            preview_image.Visible = true;
            this.Invalidate();
        }

        /// <summary>
        /// 既存のデータを破棄します。未保存時のチェックは行わない。
        /// </summary>
        private void ClearExistingData() {
            StopPauseCore(); //timerPreview.Enabled = false;
            previewer.TrackBarValue = 0;
            previewer.TrackBarMaximum = 0;
            AppManager.SaveData.m_zorder.Clear();
            AppManager.SaveData.m_group_vsq.Clear();
            AppManager.SaveData.m_groups_character.Clear();
            AppManager.SaveData.m_group_another.Clear();
            for ( int i = 0; i < AppManager.SaveData.m_group_plugin.Count; i++ ) {
                AppManager.SaveData.m_group_plugin[i].Clear();
                AppManager.SaveData.m_plugins_config[i].Config = "";
            }
            AppManager.ClearCommandBuffer();
            AppManager.SaveData.m_totalSec = 0.0f;
            m_not_used_plugin.Clear();
            m_not_used_plugin_config.Clear();
            m_player.Close();
            AppManager.SaveData.m_telop_ex2.Clear();
            AppManager.MaxTelopLanes = 0;
            property.Editing = null;
            AppManager.SaveData.REPEAT_START = 0f;
            AppManager.SaveData.REPEAT_END = -1f;
            if ( AppManager.SaveData.m_timesig_ex != null ) {
                AppManager.SaveData.m_timesig_ex.Clear();
            }
            if ( AppManager.SaveData.m_tempo != null ) {
                AppManager.SaveData.m_tempo.Clear();
            }
            m_curve.Clear();
            menuEditRedo.Enabled = false;
            menuEditUndo.Enabled = false;
            UpdateObjectList();
        }

        /// <summary>
        /// 指定されたトラックから、歌詞のタイムラインを読み込み、現在のvsqトラックリスト、
        /// s.m_group_vsqに追加します。
        /// </summary>
        /// <param name="track">追加するトラック</param>
        private void addTimeLineFromTrack( VsqFile vsqFile, int track_number, bool enable_undo ) {
            string tmp_lyric = Path.GetTempFileName();
            vsqFile.printLyricTable( track_number, tmp_lyric );
            using ( TimeTable temp = new TimeTable( vsqFile.Track[track_number].Name, 0, TimeTableType.vsq, null ) ) {
                using ( StreamReader sr = new StreamReader( tmp_lyric ) ) {
                    string line;
                    while ( sr.Peek() >= 0 ) {
                        line = sr.ReadLine();
                        string[] spl = line.Split( new char[] { ',' } );
                        float begin = float.Parse( spl[1] );
                        float end = float.Parse( spl[2] );
                        string body = spl[3] + "(" + spl[4] + ")";
                        temp.Add( new TimeTableEntry( begin, end, body ) );
                    }
                }
                Command run = Command.GCommandAddTimeTable( TimeTableType.vsq, -1, AppManager.SaveData.m_group_vsq.Count, temp );
                Command inv = AppManager.SaveData.Execute( run );
                if ( enable_undo ) {
                    AppManager.Register( inv );
                }

            }

            File.Delete( tmp_lyric );
            AppManager.Edited = true;
        }

        private void AddTimeLineFromUst( UstFile ust_file, int track_number, bool enable_undo ) {
            TimeTable temp = new TimeTable( ust_file.getProjectName(), 0, TimeTableType.vsq, null );
            int count = 0;
            for ( int i = 0; i < ust_file.getTrack( track_number ).getEventCount(); i++ ) {
                UstEvent ue = ust_file.getTrack( track_number ).getEvent( i );
                float start = (float)ust_file.getSecFromClock( count );
                float end = (float)ust_file.getSecFromClock( (int)(count + ue.Length) );
                string phrase = ue.Lyric;
                if ( phrase != "R" ) {
                    string symbol = "a";
                    if ( !SymbolTable.attatch( phrase, out symbol ) ) {
                        symbol = "a";
                    }
                    temp.Add( new TimeTableEntry( start, end, phrase + "(" + symbol + ")" ) );
                }
                count += (int)ue.Length;
            }
            Command run = Command.GCommandAddTimeTable( TimeTableType.vsq, -1, AppManager.SaveData.m_group_vsq.Count, temp );
            Command inv = AppManager.SaveData.Execute( run );
            if ( enable_undo ) {
                AppManager.Register( inv );
            }
        }

        /// <summary>
        /// VSQファイルのトラック情報から、口の動きのタイムラインを作成します
        /// </summary>
        /// <param name="vsqTrack"></param>
        private void GenerateLipsyncFromVsq( TimeTable time_table, Character3 character, bool enable_undo ) {
            string name = character.Name;
            TimeTableGroup temp = new TimeTableGroup( name, 0, character );

            TimeTableGroup.GenerateLipSyncFromVsq( time_table,
                                                   ref temp,
                                                   character,
                                                   AppManager.SaveData.m_totalSec,
                                                   AppManager.Config.CloseMouthWhenSameVowelsRepeated,
                                                   AppManager.SaveData.FrameRate,
                                                   AppManager.Config.EntryCombineThreshold );

            Command run = Command.GCommandAddGroup( TimeTableType.character, 
                                                    AppManager.SaveData.m_groups_character.Count,
                                                    temp );
            Command inv = AppManager.SaveData.Execute( run );
            if ( enable_undo ) {
                AppManager.Register( inv );
            }
            temp.Dispose();

            if ( enable_undo ) {
                AppManager.Edited = true;
            }
            UpdateObjectList();
            AppManager.SaveData.UpdateZorder();
        }

        private DialogResult requestIntention() {
            if ( m_filePath != "" ) {
                return MessageBox.Show( "'" + Path.GetFileName( m_filePath ) + "'" + _( " has been changed. Do you wish to save changes to file?" ), "LipSync", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation );
            } else {
                return MessageBox.Show( _( "Do you wish to save changes to file?" ), "LipSync", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation );
            }
        }

        /// <summary>
        /// 操作環境の設定項目を保存します
        /// </summary>
        private void SaveConfig() {
            AppManager.Config.CloseMouthPhoneticSymbols.Clear();
            foreach ( string s in VowelType.m_list_nn ) {
                AppManager.Config.CloseMouthPhoneticSymbols.Add( s );
            }
            AppManager.Config.IMouthPhoneticSymbols.Clear();
            foreach ( string s in VowelType.m_list_i ) {
                AppManager.Config.IMouthPhoneticSymbols.Add( s );
            }
            AppManager.Config.UMouthPhoneticSymbols.Clear();
            foreach ( string s in VowelType.m_list_u ) {
                AppManager.Config.UMouthPhoneticSymbols.Add( s );
            }
            AppManager.Config.CleanUpMouthList();
            string config_file = Path.Combine( Application.StartupPath, "LipSync.config" );
            AppManager.Config.Save( config_file );
        }

        /// <summary>
        /// 操作環境の設定項目をファイルから読み込み、適用します
        /// </summary>
        private void LoadConfig() {
            string config_file = Path.Combine( Application.StartupPath, "LipSync.config" );
            AppManager.Config = EnvSettings.FromFile( config_file );
            AppManager.Config.CleanUpMouthList();
            VowelType.m_list_i.Clear();
            foreach ( string s in AppManager.Config.IMouthPhoneticSymbols ) {
                VowelType.m_list_i.Add( s );
            }
            VowelType.m_list_nn.Clear();
            foreach ( string s in AppManager.Config.CloseMouthPhoneticSymbols ) {
                VowelType.m_list_nn.Add( s );
            }
            VowelType.m_list_u.Clear();
            foreach ( string s in AppManager.Config.UMouthPhoneticSymbols ) {
                VowelType.m_list_u.Add( s );
            }

            // restore window size & location | ウィンドウ位置・サイズの復元
            bool visible = false;
            Point pt_lu = AppManager.Config.WindowPosition.Location;
            Point pt_ll = new Point( AppManager.Config.WindowPosition.Left, AppManager.Config.WindowPosition.Bottom );
            Point pt_ru = new Point( AppManager.Config.WindowPosition.Right, AppManager.Config.WindowPosition.Top );
            Point pt_rl = new Point( AppManager.Config.WindowPosition.Right, AppManager.Config.WindowPosition.Bottom );
            foreach ( Screen s in Screen.AllScreens ) {
                Rectangle r = s.Bounds;
                visible = visible | (AppManager.IsInRectangle( pt_lu, r ) | AppManager.IsInRectangle( pt_ll, r ) | AppManager.IsInRectangle( pt_ru, r ) | AppManager.IsInRectangle( pt_rl, r ));
            }
            if ( visible ) {
                this.Top = AppManager.Config.WindowPosition.Top;
                this.Left = AppManager.Config.WindowPosition.Left;
                this.Width = AppManager.Config.WindowPosition.Width;
                this.Height = AppManager.Config.WindowPosition.Height;
            } else {
                this.Width = Screen.PrimaryScreen.Bounds.Width / 2;
                this.Height = Screen.PrimaryScreen.Bounds.Height / 2;
                this.Top = this.Height / 2;
                this.Left = this.Width / 2;
            }
            if ( AppManager.Config.WindowIsMaximized ) {
                base.WindowState = FormWindowState.Maximized;
            }

            Messaging.Language = AppManager.Config.Language;
            ApplyLanguage();

            // 拡張前の設定ファイルに対応
            // いまんとこなにもなし

            previewer.PreviewSizeMode = AppManager.Config.PreviewZoomMode;

            AppManager.Config.PreviewHidden = !AppManager.Config.PreviewHidden;
            ChangePreviewHidden();
            AppManager.Config.PropertyHidden = !AppManager.Config.PropertyHidden;
            ChangePropertyHidden();

            menuVisualBars.Checked = AppManager.Config.DrawBars;
        }

        /// <summary>
        /// クオンタイズモード用のグリッドリスト「m_grids」を更新します
        /// </summary>
        private void UpdateGridList() {
            IEnumerable<BarLineType> blte = AppManager.SaveData.GetBarLineTypeEnumerator( AppManager.Config.QuantizeMode, AppManager.Config.QuantizeTripletEnabled );
            float start = m_startToDrawX / (float)AppManager.Config.PixelPerSec;
            float end = (m_startToDrawX + pictureBox1.Width) / (float)AppManager.Config.PixelPerSec;
            List<float> c = new List<float>();
            foreach ( BarLineType bar in blte ) {
                if ( start <= bar.Time && bar.Time <= end && !bar.NeverUseForSnapPoint ) {
                    c.Add( (float)bar.Time );
                } else if ( bar.Time > end ) {
                    break;
                }
            }
            m_grids = c.ToArray();
        }

        public float Now {
            get {
                if ( AppManager.Playing ) {
                    TimeSpan ts = (DateTime.Now).Subtract( m_started_date );
                    return (float)ts.TotalSeconds * m_player.Speed;
                } else {
                    int nof = previewer.TrackBarValue;
                    return nof / AppManager.SaveData.FrameRate;
                }
            }
        }

        /// <summary>
        /// タイムライン左端の，グループ折りたたみ状態を表す三角形を描画する
        /// </summary>
        /// <param name="g"></param>
        /// <param name="y"></param>
        /// <param name="folded"></param>
        private void DrawTriangle( Graphics g, int y, bool folded ) {
            SmoothingMode past = g.SmoothingMode;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            using ( SolidBrush brs = new SolidBrush( TRIANGLE_COLOR ) ) {
                using ( Pen pen = new Pen( brs ) ) {
                    pen.LineJoin = LineJoin.Round;
                    Point[] list = new Point[6];
                    if ( folded ) {
                        list[0] = new Point( 6, y + 15 );
                        list[1] = new Point( 6, y + 2 );
                        list[2] = new Point( 7, y + 2 );
                        list[3] = new Point( 13, y + 8 );
                        list[4] = new Point( 13, y + 9 );
                        list[5] = new Point( 7, y + 15 );
                    } else {
                        list[0] = new Point( 2, y + 5 );
                        list[1] = new Point( 15, y + 5 );
                        list[2] = new Point( 15, y + 6 );
                        list[3] = new Point( 9, y + 12 );
                        list[4] = new Point( 8, y + 12 );
                        list[5] = new Point( 2, y + 6 );
                    }
                    g.DrawLines( pen, list );
                    g.FillPolygon( brs, list );
                }
            }
            g.SmoothingMode = past;
        }

        /// <summary>
        /// タイムテーブルグループtableを、グラフィクスgを用いて、位置positionに描画します。
        /// 描画の結果、描かれたタイムテーブルの画面上の高さ(ピクセル)をheightに返します。
        /// </summary>
        /// <param name="g"></param>
        /// <param name="position"></param>
        /// <param name="height"></param>
        /// <param name="table"></param>
        /// <param name="title"></param>
        private void DrawTimeTableGroup( Graphics g, Point position, out int height, TimeTableGroup table, string title ) {
            //int y = position.Y;            
            Rectangle outline = new Rectangle( position.X, position.Y, pictureBox1.Width, AppManager.Config.TrackHeight );
            g.FillRectangle( new SolidBrush( AppManager.Config.TimeLineTitleColor.Color ),
                             outline );
            g.DrawString( title,
                          AppManager.Config.Font.GetFont(),
                          Brushes.Black,
                          new PointF( position.X, position.Y + AppManager.Config.VerticalStringOffset ) );
            if ( table.Folded ) {
                height = AppManager.Config.TrackHeight;
                return;
            }
            if ( table.Count > 0 ) {
                TimeTableType type = table[0].Type;
                for ( int j = 0; j < table.Count; j++ ) {
                    int i = j;
                    int y = (i + 1) * AppManager.Config.TrackHeight;
                    bool first_draw = true;
                    for ( int list_index = 0; list_index < table[i].Count; list_index++ ) {
                        int x = (int)(table[i][list_index].begin * AppManager.Config.PixelPerSec) - m_startToDrawX;
                        int xend = (int)(table[i][list_index].end * AppManager.Config.PixelPerSec) - m_startToDrawX;
                        if ( first_draw ) {
                            if ( xend >= 0 ) {
                                first_draw = false;
                            } else {
                                continue;
                            }
                        } else {
                            if ( x > pictureBox1.Width ) {
                                break;
                            }
                        }
                        int yend = y + AppManager.Config.TrackHeight;
                        Color col;
                        switch ( table[i].Type ) {
                            case TimeTableType.vsq:
                                col = AppManager.Config.TimeLineVsqColor.Color;
                                break;
                            case TimeTableType.plugin:
                                col = AppManager.Config.TimeLinePluginColor.Color;
                                break;
                            default:
                                col = AppManager.Config.TimeLineDefaultColor.Color;
                                break;
                        }
                        using ( Brush fill = new SolidBrush( col ) ) {
                            Rectangle rc = new Rectangle(
                                new Point( position.X + x, position.Y + y ),
                                new Size( xend - x, AppManager.Config.TrackHeight - 1 ) );
                            g.FillRectangle( fill, rc );
                            if ( table[i].Type == TimeTableType.plugin ) {
                                g.DrawString( "[]",
                                              AppManager.Config.Font.GetFont(),
                                              Brushes.Black,
                                              new PointF( position.X + x, position.Y + y + AppManager.Config.VerticalStringOffset ) );
                            } else if ( table[i].Type == TimeTableType.vsq ) {
                                string bd = table[i][list_index].body;
                                bd = bd.Replace( @"\\", @"\" );
                                g.DrawString( bd,
                                              AppManager.Config.Font.GetFont(),
                                              Brushes.Black,
                                              new PointF( position.X + x, position.Y + y + AppManager.Config.VerticalStringOffset ) );
                            } else {
                                g.DrawString( table[i][list_index].body,
                                              AppManager.Config.Font.GetFont(),
                                              Brushes.Black,
                                              new PointF( position.X + x, position.Y + y + AppManager.Config.VerticalStringOffset ) );
                            }
                            if ( x <= m_mousePosition.X && m_mousePosition.X < xend &&
                                 y <= m_mousePosition.Y && m_mousePosition.Y < yend ) {
                            } else {
                                g.DrawRectangle( _PEN_123_123_123, rc );
                            }
                        }
                    }

                    g.DrawString( table[i].Text,
                                  AppManager.Config.Font.GetFont(),
                                  _BRS_TRACK_NAME,
                                  new PointF( position.X, position.Y + y + AppManager.Config.VerticalStringOffset ) );
                }
            }
            height = (table.Count + 1) * AppManager.Config.TrackHeight;
        }

        /// <summary>
        /// Draw telop(s) to the specified position using specified Graphics
        /// </summary>
        /// <param name="g"></param>
        /// <param name="position"></param>
        /// <param name="height"></param>
        /// <param name="title"></param>
        private void DrawTelop( Graphics g, Point position, out int height, string title ) {
            height = (AppManager.MaxTelopLanes + 1) * AppManager.Config.TrackHeight;
            Rectangle outline = new Rectangle( position.X, position.Y, pictureBox1.Width, AppManager.Config.TrackHeight );
            g.FillRectangle( new SolidBrush( AppManager.Config.TimeLineTitleColor.Color ),
                             outline );
            g.DrawString( title,
                          AppManager.Config.Font.GetFont(),
                          Brushes.Black,
                          new PointF( position.X, position.Y + AppManager.Config.VerticalStringOffset ) );
            if ( AppManager.SaveData.TelopListFolded ) {
                height = AppManager.Config.TrackHeight;
                return;
            }
            using ( Brush fill = new SolidBrush( AppManager.Config.TimeLineDefaultColor.Color ) ) {
                for ( int i = 0; i < AppManager.SaveData.m_telop_ex2.Count; i++ ) {
                    int y = (AppManager.SaveData.m_telop_ex2[i].Lane + 1) * AppManager.Config.TrackHeight;
                    int x = (int)(AppManager.SaveData.m_telop_ex2[i].Start * AppManager.Config.PixelPerSec) - m_startToDrawX;
                    int xend = (int)(AppManager.SaveData.m_telop_ex2[i].End * AppManager.Config.PixelPerSec) - m_startToDrawX;
                    Rectangle rc = new Rectangle(
                        new Point( position.X + x, position.Y + y ),
                        new Size( xend - x, AppManager.Config.TrackHeight - 1 ) );
                    g.FillRectangle( fill, rc );
                    g.DrawString( AppManager.SaveData.m_telop_ex2[i].Text.Replace( '\n', ' ' ),
                                  AppManager.Config.Font.GetFont(),
                                  Brushes.Black, 
                                  new PointF( position.X + x, position.Y + y + AppManager.Config.VerticalStringOffset ) );
                    g.DrawRectangle( _PEN_123_123_123, rc );
                }
            }
        }

        public string _( string s ) {
            return Messaging.GetMessage( s );
        }

        private void m_text_TextChanged( object sender, EventArgs e ) {
            Size size = m_text.PreferredSize;
            if ( size.Width <= (int)m_text.Tag ) {
                size = new Size( (int)m_text.Tag, size.Height );
            }
            m_text.Size = size;
            property.Editing = new ZorderItem( "", ZorderItemType.telop, m_text_id );
            UpdateEditHandle();
        }
        
        public void UpdateEditHandle() {
            ZorderItem item = property.Selected;
            if ( item == null ) {
                return;
            }
            int index = item.Index;
            float now = Now;
            Rectangle rc;
            bool item_fixed;
            switch ( item.Type ) {
                case ZorderItemType.telop:
                    Telop telop = AppManager.SaveData[index];
                    if ( telop.Start <= now && now <= telop.End ) {
                        rc = CRectFromIRect( new Rectangle( Common.PointFromPointF( telop.GetPosition( now ) ),
                                                            Common.SizeFromSizeF( telop.ImageSize ) ),
                                             telop.GetScale( now ) );
                        item_fixed = telop.PositionFixed;
                    } else {
                        rc = new Rectangle();
                        item_fixed = false;
                    }
                    m_editing = new EditingBounds( rc, item_fixed, telop.IsXFixedAt( now ), telop.IsYFixedAt( now ) );
                    break;
                case ZorderItemType.another:
                    TimeTable table = AppManager.SaveData.m_group_another[index];
                    if ( table.IsOn( now ) ) {
                        if ( table.Image != null ) {
                            m_editing = new EditingBounds( CRectFromIRect( new Rectangle( Common.PointFromPointF( table.GetPosition( now ) ),
                                                                                          Common.SizeFromSizeF( table.ImageSize ) ),
                                                                           Math.Abs( table.Scale ) ),
                                                           table.PositionFixed,
                                                           table.IsXFixedAt( now ),
                                                           table.IsYFixedAt( now ) );
                        }
                    } else {
                        m_editing = new EditingBounds();
                    }
                    break;
                case ZorderItemType.character:
                    TimeTableGroup table_group = AppManager.SaveData.m_groups_character[index];
                    m_editing = new EditingBounds( CRectFromIRect( new Rectangle( Common.PointFromPointF( table_group.GetPosition( now ) ),
                                                                                  table_group.Character.Size ),
                                                                   Math.Abs( table_group.Scale ) ),
                                                   table_group.PositionFixed,
                                                   table_group.IsXFixedAt( now ),
                                                   table_group.IsYFixedAt( now ) );
                    break;
            }
            previewer.Invalidate();
        }

        /// <summary>
        /// ビデオ画像上の座標を、PreviewP上の座標に返還します
        /// </summary>
        /// <param name="iRect"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        private Rectangle CRectFromIRect( Rectangle iRect, float scale ) {
            Point p = iRect.Location;
            Size size = iRect.Size;
            int xx, yy, width, height;
            xx = p.X;
            yy = p.Y;
            width = (int)(size.Width * scale);
            height = (int)(size.Height * scale);
            Point iTopLeft = new Point( xx, yy );
            Point iBottomRight = new Point( xx + width, yy + height );
            Point cTopLeft = CCoordFromI( iTopLeft );
            Point cBottomRight = CCoordFromI( iBottomRight );
            return new Rectangle( cTopLeft, new Size( cBottomRight.X - cTopLeft.X, cBottomRight.Y - cTopLeft.Y ) );
        }

        private void timerPreview_Tick( object sender, EventArgs e ) {
            float now;
            if ( AppManager.Playing ) {
                now = Now;
                int nof = (int)(now * AppManager.SaveData.FrameRate);
                if ( m_is_repeat_mode ) {
                    if ( previewer.TrackBarValue < (int)(RepeatStart * AppManager.SaveData.FrameRate) || (int)(RepeatEnd * AppManager.SaveData.FrameRate) < previewer.TrackBarValue ) {
#if DEBUG
                        Common.DebugWriteLine( "trackBar1.Value,(int)(RepeatStart * s.fps)=" + previewer.TrackBarValue + "," + (int)(RepeatStart * AppManager.SaveData.FrameRate) );
#endif
                        Pause();
                        previewer.TrackBarValue = (int)(RepeatStart * AppManager.SaveData.FrameRate);
                        Play();
                        return;
                    }
                } else {
                    if ( nof > previewer.TrackBarMaximum ) {
                        StopMusic();
                        return;
                    }
                }
                if ( nof < previewer.TrackBarMinimum || previewer.TrackBarMaximum < nof ) {
                    StopMusic();
                } else {
                    previewer.TrackBarValue = nof;
                }
            } else {
                now = Now;
            }

            DateTime n = DateTime.Now;
            TimeSpan ts = n.Subtract( m_last_ignitted );
            m_last_ignitted = n;
            double diff = ts.TotalSeconds;
            for ( int i = 0; i < BUF_LEN - 1; i++ ) {
                m_buf[i] = m_buf[i + 1] + diff;
            }
            m_buf[BUF_LEN - 1] = diff;
            m_fps = BUF_LEN / m_buf[0];
            correctPosition();
            if ( m_last_key != Keys.None ) {
                AppManager.SaveData.m_groups_character[m_realtime_group][m_realtime_track][m_realtime_entry].end = Now;
            }
            previewer.LabelTimeText = now.ToString( "0.00" ) + "s";
            previewer.Invalidate();//            PreviewP.Invalidate();
            pictureBox1.Invalidate();
            //lblTime.Invalidate();
        }

        private int GetDraftStartToDrawX( int scroll_value ) {
            float ratio = ((float)(scroll_value) / (float)(hScrollBar1.Maximum - hScrollBar1.Minimum + 1 - hScrollBar1.LargeChange));
            if ( AppManager.SaveData.m_screenWidth - pictureBox1.Width >= 0 ) {
                return (int)((AppManager.SaveData.m_screenWidth - pictureBox1.Width) * ratio);
            } else {
                return 0;
            }
        }

        /// <summary>
        /// プレビューとタイムテーブルがシンクロするように、hScrollBar1.Valueを変更します
        /// </summary>
        private void correctPosition() {
            if ( menuVisualSync.Checked ) {
                if ( AppManager.Config.SyncAtCentre ) {
                    float now = Now;// t * m_player.Speed * 0.001f;
                    int draft_start_to_draw_x = (int)(now * AppManager.Config.PixelPerSec) - pictureBox1.Width / 2;
                    int scroll_value = (int)(draft_start_to_draw_x * (float)(hScrollBar1.Maximum - hScrollBar1.Minimum + 1 - hScrollBar1.LargeChange) / (AppManager.SaveData.m_screenWidth - pictureBox1.Width));
                    if ( scroll_value < hScrollBar1.Minimum ) {
                        scroll_value = hScrollBar1.Minimum;
                    } else if ( scroll_value > hScrollBar1.Maximum - hScrollBar1.LargeChange ) {
                        scroll_value = hScrollBar1.Maximum - hScrollBar1.LargeChange;
                    }
                    hScrollBar1.Value = scroll_value;
                    m_startToDrawX = StartToDrawX();
                } else {
                    int new_value;
                    int draft_hScrollBar1_Value = hScrollBar1.Value;        // hScrollBar1.Valueの候補値。correctPositionが続けて呼ばれるのを防ぐため。
                    int draft_start_to_draw_x = m_startToDrawX;
                    float t_start = m_startToDrawX / AppManager.Config.PixelPerSec;
                    float t_end = t_start + pictureBox1.Width / AppManager.Config.PixelPerSec;
                    float band_width = (t_end - t_start) * SIDE_WIDTH;
                    float now = Now;
                    int count = 0;
                    while ( t_end - band_width < now || now < t_start ) {  // 次のpreviewTimer.TickでcorrectPositionが呼ばれないように。
                        count++;
                        if ( count > 100 ) {
                            break;
                        }
                        if ( t_end - band_width < now ) {
                            // 次の画面へスクロールする必要がある。
                            new_value = draft_hScrollBar1_Value + (int)((t_end - t_start - band_width) * AppManager.Config.PixelPerSec);
                            if ( new_value <= hScrollBar1.Maximum + 1 - hScrollBar1.LargeChange ) {
                                draft_hScrollBar1_Value = new_value;
                            } else {
                                draft_hScrollBar1_Value = hScrollBar1.Maximum + 1 - hScrollBar1.LargeChange;
                            }
                            draft_start_to_draw_x = GetDraftStartToDrawX( draft_hScrollBar1_Value );
                        } else if ( now < t_start ) {
                            // 前の画面へスクロールする必要がある
                            new_value = draft_hScrollBar1_Value - (int)((t_end - t_start - band_width) * AppManager.Config.PixelPerSec);
                            if ( new_value >= hScrollBar1.Minimum ) {
                                draft_hScrollBar1_Value = new_value;
                            } else {
                                draft_hScrollBar1_Value = hScrollBar1.Minimum;
                            }
                            draft_start_to_draw_x = GetDraftStartToDrawX( draft_hScrollBar1_Value );
                        }
                        t_start = draft_start_to_draw_x / AppManager.Config.PixelPerSec;
                        t_end = t_start + pictureBox1.Width / AppManager.Config.PixelPerSec;
                        band_width = (t_end - t_start) * SIDE_WIDTH;
                        now = Now;
                    }
                    hScrollBar1.Value = draft_hScrollBar1_Value;
                    m_startToDrawX = StartToDrawX();
                }
            }
        }

        private void UpdateScreenCore() {
            timerPreview_Tick( null, null );
            Application.DoEvents();
        }

        private void UpdateScreen() {
            while ( true ) {
                this.Invoke( new ForceScreenUpdateDelegate( UpdateScreenCore ) );
                Application.DoEvents();
            }
        }

        private void Play() {
            m_editing_item = null;
            m_editing = new EditingBounds();

            Size size = AppManager.SaveData.m_movieSize;
            previewer.PlayPauseText = _( "Pause" );
            m_current_frame = previewer.TrackBarValue;
            initializeFirstEntry();

            bool ret = m_player.PlayFrom( ((double)m_current_frame) / AppManager.SaveData.FrameRate );
#if DEBUG
            Common.DebugWriteLine( "m_current_frame=" + m_current_frame );
            Common.DebugWriteLine( "AppManager.SaveData.FrameRate=" + AppManager.SaveData.FrameRate );
#endif
            m_started_date = DateTime.Now;
            //timerPreview.Enabled = true;
            m_preview_thread = new Thread( new ThreadStart( this.UpdateScreen ) );
            m_preview_thread.Priority = ThreadPriority.BelowNormal;
            m_preview_thread.Start();
            m_started_date = m_started_date.AddSeconds( -Now / (m_player.Speed) );

            //m_fit_centre_a = 3f * (Now * m_config.PIXEL_PER_SEC - m_startToDrawX - pictureBox1.Width / 2) / (FIT_CENTER_BREND_TIME * FIT_CENTER_BREND_TIME * FIT_CENTER_BREND_TIME);
            //m_fit_centre_init_starttodrawx = m_startToDrawX;
#if DEBUG
            //Common.DebugWriteLine( "Play; m_fit_centre_a=" + m_fit_centre_a );
#endif

            AppManager.Playing = true;
            if ( menuEditRealTime.Checked ) {
                pictureBox1.Focus();
            }
            m_rcHilight = new Rectangle();
        }

        private void Pause() {
            pauseMusic();
            UpdateGridList();
        }

        private void pauseMusic() {
            StopPauseCore();
            m_player.Pause();
        }

        /// <summary>
        /// 検索開始インデックスの値をすべて0にそろえます。
        /// </summary>
        public void initializeFirstEntry() {
            for ( int track = 0; track < AppManager.SaveData.m_group_another.Count; track++ ) {
                AppManager.SaveData.m_group_another[track].Value = 0;
            }
            for ( int group = 0; group < AppManager.SaveData.m_groups_character.Count; group++ ) {
                for ( int track = 0; track < AppManager.SaveData.m_groups_character[group].Count; track++ ) {
                    AppManager.SaveData.m_groups_character[group][track].Value = 0;
                }
            }
            for ( int track = 0; track < AppManager.SaveData.m_group_plugin.Count; track++ ) {
                AppManager.SaveData.m_group_plugin[track].Value = 0;
            }
        }

        public void StopPauseCore() {
            previewer.PlayPauseText = _( "Play" );
            if ( m_preview_thread != null ) {
                m_preview_thread.Abort();
                m_preview_thread = null;
            }
            AppManager.Playing = false;
        }

        private void StopMusic() {
            StopPauseCore();
            m_player.Stop();
            previewer.TrackBarValue = 0;
        }

        /// <summary>
        /// PreviewP上の位置座標から、対応する画像の位置座標を求めます
        /// </summary>
        /// <param name="cCoord"></param>
        /// <returns></returns>
        private Point ICoordFromC( Point cCoord ) {
            int x = cCoord.X;
            int y = cCoord.Y;
            float base_x, base_y;
            float scale;
            GetScaleAndOrigin( out base_x, out base_y, out scale );
            x = (int)(x / scale + base_x);
            y = (int)(y / scale + base_y);
            return new Point( x, y );
        }

        private Point CCoordFromI( PointF iCoord ) {
            float x = iCoord.X;
            float y = iCoord.Y;
            float base_x, base_y;
            float scale;
            GetScaleAndOrigin( out base_x, out base_y, out scale );
            x = (x - base_x) * scale;
            y = (y - base_y) * scale;
            return new Point( (int)x, (int)y );
        }

        /// <summary>
        /// 画面の左上の座標を、画像の座標系で見たときの座標に変換します
        /// </summary>
        /// <param name="origin_x"></param>
        /// <param name="origin_y"></param>
        /// <param name="scale"></param>
        private void GetScaleAndOrigin( out float origin_x, out float origin_y, out float scale ) {
            if ( previewer.PreviewSizeMode == PictureBoxSizeMode.CenterImage ) {
                scale = 1.0f;
                origin_x = (previewer.Image.Width - previewer.PreviewWidth) / 2f;
                origin_y = (previewer.Image.Height - previewer.PreviewHeight) / 2f;
            } else {
                float aspecto_image = ((float)previewer.Image.Width) / ((float)previewer.Image.Height);
                float aspecto_control = ((float)previewer.PreviewWidth) / ((float)previewer.PreviewHeight);
                if ( aspecto_control > aspecto_image ) {
                    scale = ((float)previewer.PreviewHeight) / ((float)previewer.Image.Height);
                    origin_x = (previewer.Image.Width * scale - previewer.PreviewWidth) / 2f / scale;
                    origin_y = 0;
                } else {
                    scale = ((float)previewer.PreviewWidth) / ((float)previewer.Image.Width);
                    origin_x = 0;
                    origin_y = (previewer.Image.Height * scale - previewer.PreviewHeight) / 2f / scale;
                }
            }
        }

        private void showDialogCore( bool is_raw_mode ) {
            using ( AviOutput aviOutput = new AviOutput( is_raw_mode ) ) {
                if ( aviOutput.ShowDialog() == DialogResult.OK ) {
                    AviWriting = true;
                    m_avi_cancel = false;
                    AviOutputArguments args = aviOutput.Arguments;
                    args.IsRawMode = is_raw_mode;
                    bgWorkAvi.RunWorkerAsync( args );
                }
            }
        }

        private void ChangeFormTitle( string encoder_type, int percent, int current_frame, int max_frames ) {
            this.Text = encoder_type + " " + _( "Progress" ) + " " + percent + "% [" + current_frame + "/" + max_frames + "]";
        }

        private void FFMpegOutputRead( object start_args ) {
            // frame=  151 fps=  7 q=12.5 Lsize=     568kB time=5.0 bitrate= 930.6kbits/s
            long max_frames = 1;
            if ( start_args is long ) {
                max_frames = (long)start_args;
            }
            StreamReader reader = this.m_ffmpeg.StandardError;//.StandardOutput;
            string line = "";
            while ( !this.m_ffmpeg.HasExited ) {
                char ch = (char)reader.Read();
                if ( char.IsControl( ch ) ) {
                    if ( line.StartsWith( "frame=" ) ) {
                        line = line.Substring( 6 );    // line="  151 fps=  7 q=12.5 Lsize=     568kB time=5.0 bitrate= 930.6kbits/s"
                        line = line.Replace( " ", "" );// line="151fps=7q=12.5Lsize=568kBtime=5.0bitrate=930.6kbits/s"
                        string[] spl = line.Split( "fps=".ToCharArray(), 2 );
                        if ( spl.Length > 0 ) {
                            line = spl[0]; // s="151"
                            line = line.Trim();
                            int current_frame = 1;
                            int i;
                            if ( int.TryParse( line, out i ) ) {
                                current_frame = i;
                            }
                            int percent = (int)(current_frame / (float)max_frames * 100f);
                            this.Invoke( new ChangeTitleTextDelegate( this.ChangeFormTitle ), new object[] { "ffmpeg", percent, current_frame, (int)max_frames } );
                        }
                    }
                    line = "";
                } else {
                    line += ch.ToString();
                }
                if ( m_avi_cancel ) {
                    this.m_ffmpeg.Kill();
                    m_avi_cancel = false;
                }
            }
            reader.Close();
        }

        private void MEncoderOutputRead( object start_args ) {
            long max_frames = 1;
            if ( start_args is long ) {
                max_frames = (long)start_args;
            }
            StreamReader reader = this.m_mencoder.StandardOutput;
            string line = "";
            while ( !this.m_mencoder.HasExited ) {
                char ch = (char)reader.Read();
                if ( char.IsControl( ch ) ) {
                    if ( line.StartsWith( "Pos:" ) ) {
                        line = line.Substring( 4 );  //    1.4s     42f (13%)  3.04fps Trem:   1min   1mb  A-V:0.000 [1516:0]
                        line = line.Replace( " ", "" );//1.4s42f(13%)3.04fpsTrem:1min1mbA-V:0.000[1516:0]
                        string[] spl = line.Split( "f".ToCharArray(), 2 );
                        if ( spl.Length > 0 ) {
                            line = spl[0]; // s="1.4s42"
                            spl = line.Split( "s".ToCharArray(), 2 );
                            if ( spl.Length > 1 ) {
                                line = spl[1]; // line="42"
                                int current_frame = 1;
                                int i;
                                if ( int.TryParse( line, out i ) ) {
                                    current_frame = i;
                                }
                                int percent = (int)(current_frame / (float)max_frames * 100f);
                                this.Invoke( new ChangeTitleTextDelegate( this.ChangeFormTitle ), new object[] { "mencoder", percent, current_frame, (int)max_frames } );
                            }
                        }
                    }
                    line = "";
                } else {
                    line += ch.ToString();
                }
                //mencoderが出力する進捗の形式
                //Pos:   1.4s     42f (13%)  3.04fps Trem:   1min   1mb  A-V:0.000 [1516:0]
                if ( m_avi_cancel ) {
                    this.m_mencoder.Kill();
                    m_avi_cancel = false;
                }
            }
            reader.Close();
        }

        /// <summary>
        /// m_curveとproperty1のリストボックスの中身を更新します
        /// </summary>
        public void UpdateObjectList() {
            List<ListViewItem> adding = new List<ListViewItem>();
            bool exist = false;
            ZorderItem zi = property.Editing;
            foreach ( ZorderItem item in AppManager.SaveData.GetZorderItemEnumerator() ) {
                if ( zi != null ) {
                    if ( zi.Type == item.Type && zi.Index == item.Index ) {
                        exist = true;
                    }
                }
                if ( item.Type == ZorderItemType.character ) {
                    int i = item.Index;
                    float start = 0f;
                    string[] titles = new string[] { start.ToString(), "character", AppManager.SaveData.m_groups_character[i].Text };
                    ListViewItem add_item = new ListViewItem( titles );
                    item.Start = start;
                    add_item.Tag = (ZorderItem)item.Clone();
                    adding.Add( add_item );
                } else if ( item.Type == ZorderItemType.another ) {
                    int i = item.Index;
                    float start = AppManager.SaveData.m_group_another.GetFirstOn( i );
                    string[] titles = new string[] { start.ToString(),
                                                     "another",
                                                     AppManager.SaveData.m_group_another[i].Text };
                    ListViewItem add_item = new ListViewItem( titles );
                    item.Start = start;
                    add_item.Tag = (ZorderItem)item.Clone();
                    adding.Add( add_item );
                } else if ( item.Type == ZorderItemType.telop ) {
                    int i = item.Index;
                    float start = AppManager.SaveData[i].Start;
                    string[] titles = new string[]{ start.ToString(),
                                                    "telop",
                                                    AppManager.SaveData[i].Text };
                    ListViewItem add_item = new ListViewItem( titles );
                    item.Start = start;
                    add_item.Tag = (ZorderItem)item.Clone();
                    adding.Add( add_item );
                }
            }
            property.ListView.Items.Clear();
            property.ListView.Items.AddRange( adding.ToArray() );
            property.Sort();
            if ( !exist ) {
                property.Editing = null;
                property.SelectedObject = null;
            }

            m_curve.comboObjects.Items.Clear();
            for ( int i = 0; i < AppManager.SaveData.m_groups_character.Count; i++ ) {
                m_curve.comboObjects.Items.Add( new TagForTreeNode( ZorderItemType.character, i ) );
            }
            for ( int i = 0; i < AppManager.SaveData.m_group_another.Count; i++ ) {
                m_curve.comboObjects.Items.Add( new TagForTreeNode( ZorderItemType.another, i ) );
            }
            for ( int i = 0; i < AppManager.SaveData.m_telop_ex2.Count; i++ ) {
                m_curve.comboObjects.Items.Add( new TagForTreeNode( ZorderItemType.telop, AppManager.SaveData.m_telop_ex2[i].ID ) );
            }

        }

        /// <summary>
        /// マウスの現在位置を元に、ハイライトするべき部分のRectangleを取得します
        /// mouse_positionまたはX, Yには、pictureBox1上のマウス位置を与えなければならない
        /// </summary>
        /// <param name="mouse_position"></param>
        /// <returns>ハイライトするべき範囲を表すRectangle。Paintイベントで素直に描けばOK</returns>
        private Rectangle GetHilightRect( int X, int Y ) {
            int start_to_draw_x = m_startToDrawX;// StartToDrawX;
            int start_to_draw_y = m_startToDrawY;// StartToDrawY;
            Item clicked = GetGroupItem( X + start_to_draw_x, Y + start_to_draw_y );
            TimeTableType type = clicked.type;
            int group = clicked.group;
            int track = clicked.track;
            int row_index = clicked.row_index;
            if ( group < 0 ) {
                return new Rectangle();
            }
            bool vsq_fixed = menuVisualVsqTrack.Checked;
            if ( track < 0 ) {
                int group_tracks = 0;
                if ( clicked.type == TimeTableType.telop ) {
                    if ( AppManager.SaveData.TelopListFolded ) {
                        group_tracks = 0;
                    } else {
                        group_tracks = AppManager.MaxTelopLanes;
                    }
                } else {
                    if ( AppManager.SaveData[clicked.type, group].Folded ) {
                        group_tracks = 0;
                    } else {
                        group_tracks = AppManager.SaveData[clicked.type, group].Count;
                    }
                }
                if ( clicked.type == TimeTableType.vsq && vsq_fixed ) {
                    return new Rectangle( 0, row_index * AppManager.Config.TrackHeight, pictureBox1.Width, (group_tracks + 1) * AppManager.Config.TrackHeight );
                } else {
                    return new Rectangle( 0, row_index * AppManager.Config.TrackHeight - start_to_draw_y, pictureBox1.Width, (group_tracks + 1) * AppManager.Config.TrackHeight );
                }
            } else {
                int y = row_index * AppManager.Config.TrackHeight - start_to_draw_y;
                Rectangle current;
                switch ( clicked.type ) {
                    case TimeTableType.vsq:
                        int yy = y + m_startToDrawY;
                        for ( int entry = 0; entry < AppManager.SaveData.m_group_vsq[track].Count; entry++ ) {
                            int x = (int)(AppManager.SaveData.m_group_vsq[track][entry].begin * AppManager.Config.PixelPerSec) - start_to_draw_x;
                            int xend = (int)(AppManager.SaveData.m_group_vsq[track][entry].end * AppManager.Config.PixelPerSec) - start_to_draw_x;
                            if ( vsq_fixed ) {
                                current = new Rectangle( x, yy, xend - x, AppManager.Config.TrackHeight );
                                if ( AppManager.IsInRectangle( new Point( X, Y ), current ) ) {
                                    return current;
                                }
                            } else {
                                current = new Rectangle( x, y, xend - x, AppManager.Config.TrackHeight );
                                if ( AppManager.IsInRectangle( new Point( X, Y ), current ) ) {
                                    return current;
                                }
                            }
                        }
                        if ( vsq_fixed ) {
                            return new Rectangle( 0, yy, pictureBox1.Width, AppManager.Config.TrackHeight );
                        } else {
                            return new Rectangle( 0, y, pictureBox1.Width, AppManager.Config.TrackHeight );
                        }
                    case TimeTableType.character:
                    case TimeTableType.another:
                    case TimeTableType.plugin:
                        for ( int entry = 0; entry < AppManager.SaveData[type, group][track].Count; entry++ ) {
                            int x = (int)(AppManager.SaveData[type, group][track][entry].begin * AppManager.Config.PixelPerSec) - start_to_draw_x;
                            int xend = (int)(AppManager.SaveData[type, group][track][entry].end * AppManager.Config.PixelPerSec) - start_to_draw_x;
                            current = new Rectangle( x, y, xend - x, AppManager.Config.TrackHeight );
                            if ( AppManager.IsInRectangle( new Point( X, Y ), current ) ) {
                                return current;
                            }
                        }
                        return new Rectangle( 0, y, pictureBox1.Width, AppManager.Config.TrackHeight );
                    case TimeTableType.telop:
                        for ( int i = 0; i < AppManager.SaveData.m_telop_ex2.Count; i++ ) {
                            if ( AppManager.SaveData.m_telop_ex2[i].ID == clicked.entry ) {
                                int x = (int)(AppManager.SaveData.m_telop_ex2[i].Start * AppManager.Config.PixelPerSec) - start_to_draw_x;
                                int xend = (int)(AppManager.SaveData.m_telop_ex2[i].End * AppManager.Config.PixelPerSec) - start_to_draw_x;
                                return new Rectangle( x, y, xend - x, AppManager.Config.TrackHeight );
                            }
                        }
                        return new Rectangle();
                    default:
                        return new Rectangle();
                }

            }
        }
    }

}
