\83¡ã\82¤ã\83³ã\83\95ã\82©ã\83¼ã\83 .cs - RSS feed" href="/view?p=strokestylet/CsWin10Desktop3.git;a=rss;f=SSTFEditor/%C3%A3%C2%83%C2%A1%C3%A3%C2%82%C2%A4%C3%A3%C2%83%C2%B3%C3%A3%C2%83%C2%95%C3%A3%C2%82%C2%A9%C3%A3%C2%83%C2%BC%C3%A3%C2%83%C2%A0.cs" type="application/rss+xml" /> \83¡ã\82¤ã\83³ã\83\95ã\82©ã\83¼ã\83 .cs - RSS feed (no merges)" href="/view?p=strokestylet/CsWin10Desktop3.git;a=rss;f=SSTFEditor/%C3%A3%C2%83%C2%A1%C3%A3%C2%82%C2%A4%C3%A3%C2%83%C2%B3%C3%A3%C2%83%C2%95%C3%A3%C2%82%C2%A9%C3%A3%C2%83%C2%BC%C3%A3%C2%83%C2%A0.cs;opt=--no-merges" type="application/rss+xml" /> \83¡ã\82¤ã\83³ã\83\95ã\82©ã\83¼ã\83 .cs - Atom feed" href="/view?p=strokestylet/CsWin10Desktop3.git;a=atom;f=SSTFEditor/%C3%A3%C2%83%C2%A1%C3%A3%C2%82%C2%A4%C3%A3%C2%83%C2%B3%C3%A3%C2%83%C2%95%C3%A3%C2%82%C2%A9%C3%A3%C2%83%C2%BC%C3%A3%C2%83%C2%A0.cs" type="application/atom+xml" /> \83¡ã\82¤ã\83³ã\83\95ã\82©ã\83¼ã\83 .cs - Atom feed (no merges)" href="/view?p=strokestylet/CsWin10Desktop3.git;a=atom;f=SSTFEditor/%C3%A3%C2%83%C2%A1%C3%A3%C2%82%C2%A4%C3%A3%C2%83%C2%B3%C3%A3%C2%83%C2%95%C3%A3%C2%82%C2%A9%C3%A3%C2%83%C2%BC%C3%A3%C2%83%C2%A0.cs;opt=--no-merges" type="application/atom+xml" />

OSDN Git Service

Merge branch 'develop' into SSTFEditorでデバイス情報保存に対応する
[strokestylet/CsWin10Desktop3.git] / SSTFEditor / ã\83¡ã\82¤ã\83³ã\83\95ã\82©ã\83¼ã\83 .cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Drawing;
5 using System.IO;
6 using System.Linq;
7 using System.Reflection;
8 using System.Windows.Forms;
9 using FDK;      // for string拡張
10
11 namespace SSTFEditor
12 {
13         partial class メインフォーム : Form
14         {
15                 public int メジャーバージョン番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Major );
16                 public int マイナーバージョン番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Minor );
17                 public int リビジョン番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Revision );
18                 public int ビルド番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Build );
19
20                 public const int 最大音量 = 4;
21                 public const int 最小音量 = 1;
22
23                 public bool 未保存である
24                 {
25                         get
26                         {
27                                 return this.bs_未保存である;
28                         }
29                         set
30                         {
31                                 // まず値を保存。
32                                 this.bs_未保存である = value;
33
34                                 // ウィンドウのタイトルバーの文字列を修正する。
35                                 string 表示するファイルの名前 = ( string.IsNullOrEmpty( this.編集中のファイル名 ) ) ? Properties.Resources.NEW_FILENAME : this.編集中のファイル名;
36                                 if( this.bs_未保存である )
37                                 {
38                                         // 変更ありかつ未保存なら「*」を付ける。
39                                         this.Text = $"SSTFEditor {this.メジャーバージョン番号}.{this.マイナーバージョン番号}.{this.リビジョン番号}.{this.ビルド番号} *[{表示するファイルの名前}]";
40                                         this.toolStripMenuItem上書き保存.Enabled = true;
41                                         this.toolStripButton上書き保存.Enabled = true;
42                                 }
43                                 else
44                                 {
45                                         // 保存後変更がないなら「*」は付けない。
46                                         this.Text = $"SSTFEditor {this.メジャーバージョン番号}.{this.マイナーバージョン番号}.{this.リビジョン番号}.{this.ビルド番号} [{表示するファイルの名前}]";
47                                         this.toolStripMenuItem上書き保存.Enabled = false;
48                                         this.toolStripButton上書き保存.Enabled = false;
49                                 }
50                         }
51                 }
52                 public bool 選択モードである => ( ( CheckState.Checked == this.toolStripButton選択モード.CheckState ) ? true : false );
53                 public bool 編集モードである => ( ( CheckState.Checked == this.toolStripButton編集モード.CheckState ) ? true : false );
54
55                 public int GRID_PER_PART { get; protected set; }
56                 public int GRID_PER_PIXEL => ( this.bs_GRID_PER_PIXEL / ( this.toolStripComboBox譜面拡大率.SelectedIndex + 1 ) );
57
58                 public bool 初期化完了 = false;
59                 public Config Config;
60                 public 選択モード 選択モード;
61                 public 編集モード 編集モード;
62                 public C譜面 譜面;
63                 public UndoRedo.管理 UndoRedo管理;
64                 public クリップボード クリップボード;
65                 public Size 譜面パネルサイズ => ( this.pictureBox譜面パネル.ClientSize );
66                 public SSTFormat.チップ種別 現在のチップ種別
67                 {
68                         get
69                         {
70                                 return this.bs_e現在のチップ種別; 
71                         }
72                         set
73                         {
74                                 this.bs_e現在のチップ種別 = value;
75                                 this.label現在のチップ種別.Text = this.チップ種別toチップ名対応表[ value ];
76                         }
77                 }
78                 public int 現在のチップ音量 = メインフォーム.最大音量;
79
80                 public メインフォーム()
81                 {
82                         InitializeComponent();
83
84                         this.Actアプリを起動する();
85                 }
86
87                 public void 選択モードに切替えて関連GUIを設定する()
88                 {
89                         this.toolStripButton選択モード.CheckState = CheckState.Checked;
90                         this.toolStripButton編集モード.CheckState = CheckState.Unchecked;
91
92                         this.toolStripMenuItem選択モード.CheckState = CheckState.Checked;
93                         this.toolStripMenuItem編集モード.CheckState = CheckState.Unchecked;
94
95                         this.label現在のチップ種別.Text = "----";
96
97                         this.譜面をリフレッシュする();
98                 }
99                 public void 編集モードに切替えて関連GUIを設定する()
100                 {
101                         this.選択モード.全チップの選択を解除する();
102                         this.譜面をリフレッシュする();
103
104                         this.toolStripButton選択モード.CheckState = CheckState.Unchecked;
105                         this.toolStripButton編集モード.CheckState = CheckState.Checked;
106
107                         this.toolStripMenuItem選択モード.CheckState = CheckState.Unchecked;
108                         this.toolStripMenuItem編集モード.CheckState = CheckState.Checked;
109                 }
110                 public void 選択チップの有無に応じて編集用GUIのEnabledを設定する()
111                 {
112                         bool 譜面上に選択チップがある = this.選択チップが1個以上ある;
113                         bool クリップボードに選択チップがある = ( null != this.クリップボード ) && ( 0 < this.クリップボード.セル数 );
114
115                         // 編集メニューの Enabled 設定
116                         this.toolStripMenuItemコピー.Enabled = 譜面上に選択チップがある;
117                         this.toolStripMenuItem切り取り.Enabled = 譜面上に選択チップがある;
118                         this.toolStripMenuItem貼り付け.Enabled = クリップボードに選択チップがある;
119                         this.toolStripMenuItem削除.Enabled = 譜面上に選択チップがある;
120
121                         // ツールバーの Enabled 設定
122                         this.toolStripButtonコピー.Enabled = 譜面上に選択チップがある;
123                         this.toolStripButton切り取り.Enabled = 譜面上に選択チップがある;
124                         this.toolStripButton貼り付け.Enabled = クリップボードに選択チップがある;
125                         this.toolStripButton削除.Enabled = 譜面上に選択チップがある;
126
127                         // 右メニューの Enabled 設定
128                         this.toolStripMenuItem選択チップのコピー.Enabled = 譜面上に選択チップがある;
129                         this.toolStripMenuItem選択チップの切り取り.Enabled = 譜面上に選択チップがある;
130                         this.toolStripMenuItem選択チップの貼り付け.Enabled = クリップボードに選択チップがある;
131                         this.toolStripMenuItem選択チップの削除.Enabled = 譜面上に選択チップがある;
132                 }
133                 public void 譜面をリフレッシュする()
134                 {
135                         this.pictureBox譜面パネル.Refresh();
136                 }
137                 public void UndoRedo用GUIのEnabledを設定する()
138                 {
139                         bool Undo可 = ( 0 < this.UndoRedo管理.Undo可能な回数 ) ? true : false;
140                         bool Redo可 = ( 0 < this.UndoRedo管理.Redo可能な回数 ) ? true : false;
141
142                         this.toolStripMenuItem元に戻す.Enabled = Undo可;
143                         this.toolStripMenuItemやり直す.Enabled = Redo可;
144                         this.toolStripButton元に戻す.Enabled = Undo可;
145                         this.toolStripButtonやり直す.Enabled = Redo可;
146                 }
147                 public void 選択モードのコンテクストメニューを表示する( int x, int y )
148                 {
149                         this.contextMenuStrip譜面右メニュー.Show( this.pictureBox譜面パネル, x, y );
150         
151                         // メニューを表示した時のマウス座標を控えておく。
152                         this.選択モードのコンテクストメニューを開いたときのマウスの位置 = new Point( x, y );
153                 }
154                 public void 譜面を縦スクロールする( int nスクロール量grid )
155                 {
156                         int 現在の位置grid = this.vScrollBar譜面用垂直スクロールバー.Value;
157                         int スクロール後の位置grid = this.vScrollBar譜面用垂直スクロールバー.Value + nスクロール量grid;
158                         int 最小値grid = this.vScrollBar譜面用垂直スクロールバー.Minimum;
159                         int 最大値grid = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
160                         
161                         if( スクロール後の位置grid < 最小値grid )
162                         {
163                                 スクロール後の位置grid = 最小値grid;
164                         }
165                         else if( スクロール後の位置grid > 最大値grid )
166                         {
167                                 スクロール後の位置grid = 最大値grid;
168                         }
169
170                         this.vScrollBar譜面用垂直スクロールバー.Value = スクロール後の位置grid;
171                 }
172
173                 protected readonly Dictionary<SSTFormat.チップ種別, string> チップ種別toチップ名対応表 = new Dictionary<SSTFormat.チップ種別, string>() {
174                         #region " *** "
175                         //-----------------
176                         { SSTFormat.チップ種別.Bass, "BassDrum" },
177                         { SSTFormat.チップ種別.BPM, "BPM" },
178                         { SSTFormat.チップ種別.China, "China" },
179                         { SSTFormat.チップ種別.HiHat_Close, "HiHat(Close)" },
180                         { SSTFormat.チップ種別.HiHat_Foot, "FootPedal" },
181                         { SSTFormat.チップ種別.HiHat_HalfOpen, "HiHat(HarfOpen)" },
182                         { SSTFormat.チップ種別.HiHat_Open, "HiHat(Open)" },
183                         { SSTFormat.チップ種別.LeftCrash, "Crash" },
184                         { SSTFormat.チップ種別.Ride, "Ride" },
185                         { SSTFormat.チップ種別.Ride_Cup, "Ride(Cup)" },
186                         { SSTFormat.チップ種別.RightCrash, "Crash" },
187                         { SSTFormat.チップ種別.Snare, "Snare" },
188                         { SSTFormat.チップ種別.Snare_ClosedRim, "Snare(CloseRimShot)" },
189                         { SSTFormat.チップ種別.Snare_Ghost, "Snare(Ghost)" },
190                         { SSTFormat.チップ種別.Snare_OpenRim, "Snare(OpenRimShot)" },
191                         { SSTFormat.チップ種別.Splash, "Splash" },
192                         { SSTFormat.チップ種別.Tom1, "HighTom" },
193                         { SSTFormat.チップ種別.Tom1_Rim, "HighTom(RimShot)" },
194                         { SSTFormat.チップ種別.Tom2, "LowTom" },
195                         { SSTFormat.チップ種別.Tom2_Rim, "LowTom(RimShot)" },
196                         { SSTFormat.チップ種別.Tom3, "FloorTom" },
197                         { SSTFormat.チップ種別.Tom3_Rim, "FloorTom(RimShot)" },
198                         { SSTFormat.チップ種別.Unknown, "" },
199                         { SSTFormat.チップ種別.小節線, "" },
200                         { SSTFormat.チップ種別.背景動画, "" },
201                         { SSTFormat.チップ種別.拍線, "" },
202                         { SSTFormat.チップ種別.小節メモ, "" },
203                         //-----------------
204                         #endregion
205                 };
206                 protected bool 選択チップが1個以上ある
207                 {
208                         get
209                         {
210                                 if( ( null != this.譜面.SSTFormatScore ) && 
211                                         ( null != this.譜面.SSTFormatScore.チップリスト ) &&
212                                         ( 0 < this.譜面.SSTFormatScore.チップリスト.Count ) )
213                                 {
214                                         foreach( var chip in this.譜面.SSTFormatScore.チップリスト )
215                                         {
216                                                 if( chip.選択が確定している )
217                                                         return true;
218                                         }
219                                 }
220                                 return false;
221                         }
222                 }
223                 protected int 現在のガイド間隔 = 0;
224                 protected Point 選択モードのコンテクストメニューを開いたときのマウスの位置;
225
226                 private bool bs_未保存である = false;
227                 private SSTFormat.チップ種別 bs_e現在のチップ種別;
228                 private int bs_GRID_PER_PIXEL = 1;
229
230                 /// <summary>
231                 /// SSTFEditor.exe と StrokeStyleT.exe が格納されているフォルダへのパス。
232                 /// </summary>
233                 protected string システムフォルダパス => ( Path.GetDirectoryName( Application.ExecutablePath ) );
234                 /// <summary>
235                 /// Windowsログインユーザのアプリデータフォルダ。
236                 /// 末尾は '\'。
237                 /// </summary>
238                 /// <remarks>
239                 /// 例: "C:\Users\ログインユーザ名\ApplicationData\SSTFEditor\"
240                 /// </remarks>
241                 protected string ユーザフォルダパス
242                 {
243                         get
244                         {
245                                 var path = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create ), @"SSTFEditor\" );
246
247                                 if( false == Directory.Exists( path ) )
248                                         Directory.CreateDirectory( path );  // なければ作成する。
249
250                                 return path;
251                         }
252                 }
253                 protected string 作業フォルダパス = null;
254                 protected string 編集中のファイル名 = null;
255                 protected string 最後にプレイヤーに渡した一時ファイル名 = null;
256
257                 // アクションメソッド(機能ツリーの最上位。メニューやコマンドバーなどから呼び出される。)
258
259                 protected void Actアプリを起動する()
260                 {
261                         this.GRID_PER_PART = int.Parse( Properties.Resources.GRID_PER_PART );
262                         this.bs_GRID_PER_PIXEL = int.Parse( Properties.Resources.GRID_PER_PIXEL );
263
264                         // 作業フォルダの初期値は、Windowsユーザのマイドキュメントフォルダ。
265                         this.作業フォルダパス = Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments );
266
267                         // Config.xml を読み込む。
268                         this.Config = Config.読み込む( Path.Combine( this.ユーザフォルダパス, Properties.Resources.CONFIG_FILE_NAME ) );
269                         if( this.Config.ViewerPath.Nullまたは空である() )
270                         {
271                                 // 既定のプレイヤーは、exe と同じ場所にあるものとする。
272                                 this.Config.ViewerPath = Path.Combine( this.システムフォルダパス, Properties.Resources.PLAYER_NAME );
273
274                                 if( false == File.Exists( this.Config.ViewerPath ) )
275                                         this.Config.ViewerPath = "";    // ビュアーが存在してない。
276                         }
277
278                         // デザイナでは追加できないイベントを手動で追加する。
279                         this.splitContainer分割パネルコンテナ.MouseWheel += new MouseEventHandler( splitContainer分割パネルコンテナ_MouseWheel );
280                         
281                         // 最近使ったファイル一覧を更新する。
282                         this.ConfigのRecentUsedFilesをファイルメニューへ追加する();
283                         
284                         // その他の初期化。
285                         this.Act新規作成する();
286                         this.Actガイド間隔を変更する( 16 );   // 初期は 1/16 間隔。
287                         this.Act譜面拡大率を変更する( 1 );    // 初期は 標準。
288
289                         // 完了。
290                         this.初期化完了 = true;
291                 }
292                 protected void Actアプリを終了する()
293                 {
294                         // 一時ファイルが残っていれば、削除する。
295                         if( File.Exists( this.最後にプレイヤーに渡した一時ファイル名 ) )
296                                 File.Delete( this.最後にプレイヤーに渡した一時ファイル名 );
297
298                         // Config.xml を保存する。
299                         this.Config.保存する( Path.Combine( this.ユーザフォルダパス, Properties.Resources.CONFIG_FILE_NAME ) );
300                         
301                         FDK.Utilities.解放する( ref this.譜面 );
302                         FDK.Utilities.解放する( ref this.選択モード );
303                 }
304                 protected void Act新規作成する()
305                 {
306                         if( DialogResult.Cancel == this.未保存なら保存する() )
307                                 return; // 保存がキャンセルされた場合はここで中断。
308
309                         this.エディタを初期化する();
310                 }
311                 protected void Act開く()
312                 {
313                         if( DialogResult.Cancel == this.未保存なら保存する() )
314                                 return; // 保存がキャンセルされた場合はここで中断。
315
316                         #region " [ファイルを開く] ダイアログでファイルを選択する。"
317                         //-----------------
318                         var dialog = new OpenFileDialog() {
319                                 Title = Properties.Resources.MSG_ファイル選択ダイアログのタイトル,
320                                 Filter = Properties.Resources.MSG_曲ファイル選択ダイアログのフィルタ,
321                                 FilterIndex = 1,
322                                 InitialDirectory = this.作業フォルダパス,
323                         };
324                         var result = dialog.ShowDialog( this );
325
326                         // メインフォームを再描画してダイアログを完全に消す。
327                         this.Refresh();
328
329                         // OKじゃないならここで中断。
330                         if( DialogResult.OK != result )
331                                 return;
332                         //-----------------
333                         #endregion
334
335                         this.ファイルを読み込む( dialog.FileName );
336                 }
337                 protected void Act指定されたファイルを開く( string ファイルパス )
338                 {
339                         if( DialogResult.Cancel == this.未保存なら保存する() )
340                                 return; // 保存がキャンセルされた場合はここで中断。
341
342                         this.ファイルを読み込む( ファイルパス );
343                 }
344                 protected void Act上書き保存する()
345                 {
346                         if( string.IsNullOrEmpty( this.編集中のファイル名 ) )
347                         {
348                                 #region " ファイル名が未設定なら、初めての保存と見なし、ファイル保存ダイアログで保存ファイル名を指定させる。"
349                                 //-----------------
350                                 string 絶対パスファイル名 = this.ファイル保存ダイアログを開いてファイル名を取得する();
351
352                                 if( string.IsNullOrEmpty( 絶対パスファイル名 ) )
353                                         return; // ファイル保存ダイアログがキャンセルされたのならここで打ち切り。
354
355                                 this.作業フォルダパス = Path.GetDirectoryName( 絶対パスファイル名 );   // ダイアログでディレクトリを変更した場合、カレントディレクトリも変更されている。
356                                 this.編集中のファイル名 = Path.GetFileName( 絶対パスファイル名 );
357                                 //-----------------
358                                 #endregion
359                         }
360
361                         this.Act上書き保存する(
362                                 Path.Combine( this.作業フォルダパス, this.編集中のファイル名 ),
363                                 一時ファイルである: false );
364                 }
365                 protected void Act上書き保存する( string ファイルの絶対パス, bool 一時ファイルである )
366                 {
367                         #region " [保存中です] ポップアップを表示する。"
368                         //-----------------
369                         var msg = new Popupメッセージ(
370                                 Properties.Resources.MSG_保存中です + Environment.NewLine +
371                                 Properties.Resources.MSG_しばらくお待ち下さい );
372                         msg.Owner = this;
373                         msg.Show();
374                         msg.Refresh();
375                         //-----------------
376                         #endregion
377
378                         try
379                         {
380                                 #region " 選択モードだったら、全チップの選択を解除する。"
381                                 //-----------------
382                                 if( this.選択モードである )
383                                         this.選択モード.全チップの選択を解除する();
384                                 //-----------------
385                                 #endregion
386                                 #region " 出力するパス内の背景動画を検索する。"
387                                 //-----------------
388                                 this.textBox背景動画.Text =
389                                         ( from ファイル名 in Directory.GetFiles( Path.GetDirectoryName( ファイルの絶対パス ) )
390                                           where SSTFormat.スコア.背景動画のデフォルト拡張子s.Any( 拡張子名 => ( Path.GetExtension( ファイル名 ).ToLower() == 拡張子名 ) )
391                                           select ファイル名 ).FirstOrDefault();  // 複数あったら、最初に見つけたほうを採用。1つも見つからなければ null。
392                                                                                                                 //-----------------
393                                 #endregion
394                                 #region " SSTFファイルを出力する。"
395                                 //-----------------
396                                 this.譜面.SSTFファイルを書き出す(
397                                         ファイルの絶対パス,
398                                         $"# Created by SSTFEditor {this.メジャーバージョン番号}.{this.マイナーバージョン番号}.{this.リビジョン番号}.{this.ビルド番号}" );
399                                 //-----------------
400                                 #endregion
401                                 #region " 出力したファイルのパスを、[ファイル]メニューの最近使ったファイル一覧に追加する。"
402                                 //-----------------
403                                 if( false == 一時ファイルである )
404                                 {
405                                         this.Config.ファイルを最近使ったファイルの一覧に追加する( ファイルの絶対パス );
406                                         this.ConfigのRecentUsedFilesをファイルメニューへ追加する();
407                                 }
408                                 //-----------------
409                                 #endregion
410                         }
411                         finally
412                         {
413                                 #region " [保存中です] ポップアップを閉じる。"
414                                 //-----------------
415                                 msg.Close();
416                                 //-----------------
417                                 #endregion
418
419                                 // 最後に、ダイアログのゴミなどを消すために再描画。
420                                 this.Refresh();
421
422                                 if( false == 一時ファイルである )
423                                         this.未保存である = false;
424                         }
425                 }
426                 protected void Act名前を付けて保存する()
427                 {
428                         #region " ユーザに保存ファイル名を入力させる。"
429                         //-----------------
430                         string 絶対パスファイル名 = this.ファイル保存ダイアログを開いてファイル名を取得する();
431                         if( string.IsNullOrEmpty( 絶対パスファイル名 ) )
432                                 return; // キャンセルされたらここで中断。
433
434                         this.作業フォルダパス = Path.GetDirectoryName( 絶対パスファイル名 );
435                         this.編集中のファイル名 = Path.GetFileName( 絶対パスファイル名 );
436                         //-----------------
437                         #endregion
438
439                         this.Act上書き保存する();
440
441                         this.未保存である = true; // ウィンドウタイトルに表示されているファイル名を変更するため、一度わざと true にする。
442                         this.未保存である = false;
443                 }
444                 protected void Act終了する()
445                 {
446                         this.Close();
447                 }
448                 protected void Act元に戻す()
449                 {
450                         // Undo する対象を UndoRedoリストから取得する。
451                         var cell = this.UndoRedo管理.Undoするセルを取得して返す();
452                         if( null == cell )
453                                 return;         // なければ中断
454
455                         // Undo を実行する。
456                         cell.Undoを実行する();
457
458                         // GUIを再描画する。
459                         this.UndoRedo用GUIのEnabledを設定する();
460                         this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
461                         this.譜面をリフレッシュする();
462                 }
463                 protected void Actやり直す()
464                 {
465                         // Redo する対象を UndoRedoリストから取得する。
466                         var cell = this.UndoRedo管理.Redoするセルを取得して返す();
467                         if( null == cell )
468                                 return; // なければ中断
469
470                         // Redo を実行する。
471                         cell.Redoを実行する();
472
473                         // GUI を再描画する。
474                         this.UndoRedo用GUIのEnabledを設定する();
475                         this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
476                         this.譜面をリフレッシュする();
477                 }
478                 protected void Act切り取る()
479                 {
480                         // 譜面にフォーカスがないなら、何もしない。
481                         if( false == this.pictureBox譜面パネル.Focused )
482                                 return;
483
484                         // 切り取り = コピー + 削除
485                         this.Actコピーする();
486                         this.Act削除する();
487                 }
488                 protected void Actコピーする()
489                 {
490                         // 譜面にフォーカスがないなら何もしない。
491                         if( false == this.pictureBox譜面パネル.Focused )
492                                 return;
493
494                         // コピーする。
495                         this.クリップボード.現在選択されているチップをボードにコピーする();
496
497                         // 画面を再描画する。
498                         this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
499                         this.譜面をリフレッシュする();
500                 }
501                 protected void Act貼り付ける( int 貼り付けを開始する譜面内絶対位置grid )
502                 {
503                         // 譜面にフォーカスがないなら何もしない。
504                         if( false == this.pictureBox譜面パネル.Focused )
505                                 return;
506
507                         this.クリップボード.チップを指定位置から貼り付ける( 貼り付けを開始する譜面内絶対位置grid );
508                 }
509                 protected void Act削除する()
510                 {
511                         // 譜面にフォーカスがないなら何もしない。
512                         if( false == this.pictureBox譜面パネル.Focused )
513                                 return;
514
515                         try
516                         {
517                                 this.UndoRedo管理.トランザクション記録を開始する();
518
519                                 // 譜面が持つすべてのチップについて、選択されているチップがあれば削除する。
520                                 for( int i = this.譜面.SSTFormatScore.チップリスト.Count - 1; 0 <= i; i-- )
521                                 {
522                                         var chip = this.譜面.SSTFormatScore.チップリスト[ i ];
523
524                                         if( chip.選択が確定している )
525                                         {
526                                                 var chip変更前 = new SSTFormat.チップ( chip );
527
528                                                 var cell = new UndoRedo.セル<SSTFormat.チップ>(
529                                                         所有者ID: null,
530                                                         Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
531                                                                 変更対象.CopyFrom( 変更前 );
532                                                                 this.譜面.SSTFormatScore.チップリスト.Add( 変更対象 );
533                                                                 this.譜面.SSTFormatScore.チップリスト.Sort();
534                                                         },
535                                                         Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
536                                                                 this.譜面.SSTFormatScore.チップリスト.Remove( 変更対象 );
537                                                                 this.未保存である = true;
538                                                         },
539                                                         変更対象: chip,
540                                                         変更前の値: chip変更前,
541                                                         変更後の値: null,
542                                                         任意1: null,
543                                                         任意2: null );
544
545                                                 this.UndoRedo管理.セルを追加する( cell );
546                                                 cell.Redoを実行する();
547                                         }
548                                 }
549                         }
550                         finally
551                         {
552                                 this.UndoRedo管理.トランザクション記録を終了する();
553
554                                 // GUI を再描画する。
555                                 this.UndoRedo用GUIのEnabledを設定する();
556                                 this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
557                                 this.譜面をリフレッシュする();
558                         }
559                 }
560                 protected void Actすべて選択する()
561                 {
562                         // 編集モードなら強制的に選択モードにする。
563                         if( this.編集モードである )
564                                 this.選択モードに切替えて関連GUIを設定する();
565
566                         this.選択モード.全チップを選択する();
567                 }
568                 protected void Act選択モードにする()
569                 {
570                         this.選択モードに切替えて関連GUIを設定する();
571                 }
572                 protected void Act編集モードにする()
573                 {
574                         this.編集モードに切替えて関連GUIを設定する();
575                 }
576                 protected void Actモードを切替える()
577                 {
578                         if( this.選択モードである )
579                         {
580                                 this.編集モードに切替えて関連GUIを設定する();
581                         }
582                         else
583                         {
584                                 this.選択モードに切替えて関連GUIを設定する();
585                         }
586                 }
587                 protected void Act検索する()
588                 {
589                         this.選択モード.検索する();    // 現在のモードに関わらず、検索はすべて選択モードオブジェクトが行う。  
590                 }
591                 protected void Actガイド間隔を変更する( int n分 )
592                 {
593                         #region " 引数チェック。"
594                         //-----------------
595                         if( !( new[] { 4, 6, 8, 12, 16, 24, 32, 48, 64, 128, 0 }.Contains( n分 ) ) )
596                                 throw new ArgumentException( $"不正なガイド間隔({n分})が指定されました。" );
597                         //-----------------
598                         #endregion
599                         #region " 新しいガイド間隔を設定する。"
600                         //-----------------
601                         this.現在のガイド間隔 = n分;
602                         this.譜面.現在のガイド間隔を変更する( n分 );    // 譜面オブジェクトとも共有。
603                                                                                         //-----------------
604                         #endregion
605                         #region " ガイド間隔関連GUI(メニュー、コンボボックス)を更新する。"
606                         //-----------------
607                         // 一度すべてのガイド間隔メニューのチェックをはずし、制定された分数のメニューだけをチェックする。 
608
609                         this.toolStripMenuItemガイド間隔4分.CheckState = CheckState.Unchecked;
610                         this.toolStripMenuItemガイド間隔6分.CheckState = CheckState.Unchecked;
611                         this.toolStripMenuItemガイド間隔8分.CheckState = CheckState.Unchecked;
612                         this.toolStripMenuItemガイド間隔12分.CheckState = CheckState.Unchecked;
613                         this.toolStripMenuItemガイド間隔16分.CheckState = CheckState.Unchecked;
614                         this.toolStripMenuItemガイド間隔24分.CheckState = CheckState.Unchecked;
615                         this.toolStripMenuItemガイド間隔32分.CheckState = CheckState.Unchecked;
616                         this.toolStripMenuItemガイド間隔48分.CheckState = CheckState.Unchecked;
617                         this.toolStripMenuItemガイド間隔64分.CheckState = CheckState.Unchecked;
618                         this.toolStripMenuItemガイド間隔128分.CheckState = CheckState.Unchecked;
619                         this.toolStripMenuItemガイド間隔フリー.CheckState = CheckState.Unchecked;
620
621                         switch( n分 )
622                         {
623                                 // Menu と ComboBox の2つを変更することでイベントが2つ発生し、最終的に
624                                 // Actガイド間隔を変更する() を立て続けに2回呼び出してしまうことになるが……、まぁよしとする。
625
626                                 case 4:
627                                         this.toolStripMenuItemガイド間隔4分.CheckState = CheckState.Checked;
628                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 0;
629                                         break;
630
631                                 case 6:
632                                         this.toolStripMenuItemガイド間隔6分.CheckState = CheckState.Checked;
633                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 1;
634                                         break;
635
636                                 case 8:
637                                         this.toolStripMenuItemガイド間隔8分.CheckState = CheckState.Checked;
638                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 2;
639                                         break;
640
641                                 case 12:
642                                         this.toolStripMenuItemガイド間隔12分.CheckState = CheckState.Checked;
643                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 3;
644                                         break;
645
646                                 case 16:
647                                         this.toolStripMenuItemガイド間隔16分.CheckState = CheckState.Checked;
648                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 4;
649                                         break;
650
651                                 case 24:
652                                         this.toolStripMenuItemガイド間隔24分.CheckState = CheckState.Checked;
653                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 5;
654                                         break;
655
656                                 case 32:
657                                         this.toolStripMenuItemガイド間隔32分.CheckState = CheckState.Checked;
658                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 6;
659                                         break;
660
661                                 case 48:
662                                         this.toolStripMenuItemガイド間隔48分.CheckState = CheckState.Checked;
663                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 7;
664                                         break;
665
666                                 case 64:
667                                         this.toolStripMenuItemガイド間隔64分.CheckState = CheckState.Checked;
668                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 8;
669                                         break;
670
671                                 case 128:
672                                         this.toolStripMenuItemガイド間隔128分.CheckState = CheckState.Checked;
673                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 9;
674                                         break;
675
676                                 case 0:
677                                         this.toolStripMenuItemガイド間隔フリー.CheckState = CheckState.Checked;
678                                         this.toolStripComboBoxガイド間隔.SelectedIndex = 10;
679                                         break;
680                         }
681                         //-----------------
682                         #endregion
683                         #region " 画面を再描画する。"
684                         //-----------------
685                         this.pictureBox譜面パネル.Invalidate();
686                         //-----------------
687                         #endregion
688                 }
689                 protected void Actガイド間隔を拡大する()
690                 {
691                         switch( this.現在のガイド間隔 )
692                         {
693                                 case 4: break;
694                                 case 6: this.Actガイド間隔を変更する( 4 ); break;
695                                 case 8: this.Actガイド間隔を変更する( 6 ); break;
696                                 case 12: this.Actガイド間隔を変更する( 8 ); break;
697                                 case 16: this.Actガイド間隔を変更する( 12 ); break;
698                                 case 24: this.Actガイド間隔を変更する( 16 ); break;
699                                 case 32: this.Actガイド間隔を変更する( 24 ); break;
700                                 case 48: this.Actガイド間隔を変更する( 32 ); break;
701                                 case 64: this.Actガイド間隔を変更する( 48 ); break;
702                                 case 128: this.Actガイド間隔を変更する( 64 ); break;
703                                 case 0: this.Actガイド間隔を変更する( 128 ); break;
704                         }
705                 }
706                 protected void Actガイド間隔を縮小する()
707                 {
708                         switch( this.現在のガイド間隔 )
709                         {
710                                 case 4: this.Actガイド間隔を変更する( 6 ); break;
711                                 case 6: this.Actガイド間隔を変更する( 8 ); break;
712                                 case 8: this.Actガイド間隔を変更する( 12 ); break;
713                                 case 12: this.Actガイド間隔を変更する( 16 ); break;
714                                 case 16: this.Actガイド間隔を変更する( 24 ); break;
715                                 case 24: this.Actガイド間隔を変更する( 32 ); break;
716                                 case 32: this.Actガイド間隔を変更する( 48 ); break;
717                                 case 48: this.Actガイド間隔を変更する( 64 ); break;
718                                 case 64: this.Actガイド間隔を変更する( 128 ); break;
719                                 case 128: this.Actガイド間隔を変更する( 0 ); break;
720                                 case 0: break;
721                         }
722                 }
723                 protected void Act譜面拡大率を変更する( int n倍 )
724                 {
725                         if( ( 1 > n倍 ) || ( 10 < n倍 ) )
726                                 throw new ArgumentException( $"不正な譜面拡大率({n倍})が指定されました。" );
727
728                         this.toolStripComboBox譜面拡大率.SelectedIndex = ( n倍 - 1 );
729                         this.譜面をリフレッシュする();
730                 }
731                 protected void Act最初から再生する()
732                 {
733                         this.Act指定された小節の先頭から再生する( 小節番号: 0 );
734                 }
735                 protected void Act現在位置から再生する()
736                 {
737                         int 位置grid;
738                         int 小節番号 = this.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面.現在のカレントラインの譜面内絶対位置grid, out 位置grid );
739                         this.Act指定された小節の先頭から再生する( 小節番号 );
740                 }
741                 protected void Act現在位置からBGMのみ再生する()
742                 {
743                         int 位置grid;
744                         int 小節番号 = this.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面.現在のカレントラインの譜面内絶対位置grid, out 位置grid );
745                         this.Act指定された小節の先頭から再生する( 小節番号, 仮想ドラムを使う: false );
746                 }
747                 protected void Act指定された小節の先頭から再生する( int 小節番号, bool 仮想ドラムを使う = true )
748                 {
749                         if( this.Config.ViewerPath.Nullまたは空である() ||
750                                 ( false == File.Exists( this.Config.ViewerPath ) ) )
751                                 return;
752
753                         // 前回のテンポラリファイルが存在していれば削除する。
754                         if( File.Exists( this.最後にプレイヤーに渡した一時ファイル名 ) )
755                                 File.Delete( this.最後にプレイヤーに渡した一時ファイル名 );
756
757                         // 譜面を新しくテンポラリファイルとして出力する。
758                         do
759                         {
760                                 this.最後にプレイヤーに渡した一時ファイル名 = Path.Combine( this.作業フォルダパス, Path.GetRandomFileName() );
761                         } while( File.Exists( this.最後にプレイヤーに渡した一時ファイル名 ) );       // 同一名のファイルが存在してたらもう一度。(まずないだろうが)
762
763                         this.Act上書き保存する( 
764                                 this.最後にプレイヤーに渡した一時ファイル名, 
765                                 一時ファイルである: true );    // 一時ファイルなので、「最近使ったファイル一覧」には残さない。
766
767                         // プレイヤーを起動する。
768                         string 仮想ドラムオプション = ( 仮想ドラムを使う ) ? @" -h" : @"";
769                         Process.Start(
770                                 this.Config.ViewerPath,
771                                 $"\"{this.最後にプレイヤーに渡した一時ファイル名}\" -p {小節番号.ToString()}{仮想ドラムオプション}" );
772                 }
773                 protected void Act再生を停止する()
774                 {
775                         if( this.Config.ViewerPath.Nullまたは空である() ||
776                                 ( false == File.Exists( this.Config.ViewerPath ) ) )
777                                 return;
778
779                         // プレイヤーを、演奏停止オプションをつけて起動する。
780                         Process.Start( this.Config.ViewerPath, @"-s" );
781                 }
782                 protected void Actオプションを設定する()
783                 {
784                         using( var dialog = new オプションダイアログ() )
785                         {
786                                 // Config の現在の値をダイアログへ反映する。
787                                 dialog.checkBoxオートフォーカス.CheckState = ( this.Config.AutoFocus ) ? CheckState.Checked : CheckState.Unchecked;
788                                 dialog.checkBox最近使用したファイル.CheckState = ( this.Config.ShowRecentUsedFiles ) ? CheckState.Checked : CheckState.Unchecked;
789                                 dialog.numericUpDown最近使用したファイルの最大表示個数.Value = this.Config.MaxOfUsedRecentFiles;
790                                 dialog.textBoxViewerPath.Text = this.Config.ViewerPath;
791
792                                 if( DialogResult.OK == dialog.ShowDialog( this ) )
793                                 {
794                                         // 決定された値をダイアログから Config に反映する。
795                                         this.Config.AutoFocus = dialog.checkBoxオートフォーカス.Checked;
796                                         this.Config.ShowRecentUsedFiles = dialog.checkBox最近使用したファイル.Checked;
797                                         this.Config.MaxOfUsedRecentFiles = (int) dialog.numericUpDown最近使用したファイルの最大表示個数.Value;
798                                         this.Config.ViewerPath = dialog.textBoxViewerPath.Text;
799
800                                         // [ファイル] メニューを修正。
801                                         this.ConfigのRecentUsedFilesをファイルメニューへ追加する();
802
803                                         // Config.xml を保存する。
804                                         this.Config.保存する( Path.Combine( this.ユーザフォルダパス, Properties.Resources.CONFIG_FILE_NAME ) );
805                                 }
806                         }
807
808                         // 画面を再描画してダイアログのゴミを消す。
809                         this.Refresh();
810                 }
811                 protected void Actバージョンを表示する()
812                 {
813                         using( var dialog = new バージョン表示ダイアログ() )
814                                 dialog.ShowDialog( this );
815                 }
816                 protected void Act小節長倍率を変更する( int 小節番号 )
817                 {
818                         double 変更後倍率 = 1.0;
819                         int 変更開始小節番号 = 小節番号;
820                         int 変更終了小節番号 = 小節番号;
821
822                         #region " 変更後の小節長倍率をユーザに入力させる。"
823                         //-----------------
824                         var db現在の小節長倍率 = this.譜面.SSTFormatScore.小節長倍率を取得する( 小節番号 );
825                         using( var dialog = new 小節長倍率入力ダイアログ( 小節番号 ) )
826                         {
827                                 dialog.倍率 = (float) db現在の小節長倍率;
828                                 dialog.後続も全部変更する = false;
829
830                                 if( DialogResult.OK != dialog.ShowDialog( this ) )  // キャンセルされたらここで中断。
831                                         return;
832
833                                 変更後倍率 = (double) dialog.倍率;
834                                 変更終了小節番号 = ( dialog.後続も全部変更する ) ? this.譜面.SSTFormatScore.最大小節番号 : 小節番号;
835                         }
836                         //-----------------
837                         #endregion
838
839                         // 変更する。
840                         try
841                         {
842                                 this.UndoRedo管理.トランザクション記録を開始する();
843
844                                 for( int i = 変更開始小節番号; i <= 変更終了小節番号; i++ )
845                                 {
846                                         var 変更前倍率 = this.譜面.SSTFormatScore.小節長倍率を取得する( i );
847
848                                         #region " 新しい小節長倍率を設定する。"
849                                         //-----------------
850                                         var cell = new UndoRedo.セル<double>(
851                                                 所有者ID: null,
852                                                 Undoアクション: ( 変更対象, 変更前, 変更後, 対象小節番号, 任意2 ) => {
853                                                         this.譜面.SSTFormatScore.小節長倍率を設定する( (int) 対象小節番号, 変更前 );
854                                                         this.未保存である = true;
855                                                 },
856                                                 Redoアクション: ( 変更対象, 変更前, 変更後, 対象小節番号, 任意2 ) => {
857                                                         this.譜面.SSTFormatScore.小節長倍率を設定する( (int) 対象小節番号, 変更後 );
858                                                         this.未保存である = true;
859                                                 },
860                                                 変更対象: 0.0,
861                                                 変更前の値: 変更前倍率,
862                                                 変更後の値: 変更後倍率,
863                                                 任意1: i,
864                                                 任意2: null );
865
866                                         this.UndoRedo管理.セルを追加する( cell );
867                                         cell.Redoを実行する();
868                                         //-----------------
869                                         #endregion
870                                         #region " チップを移動または削除する。"
871                                         //-----------------
872                                         int 変化量grid = (int) ( ( 変更後倍率 - 変更前倍率 ) * this.GRID_PER_PART );
873
874                                         for( int j = this.譜面.SSTFormatScore.チップリスト.Count - 1; j >= 0; j-- )    // 削除する場合があるので後ろからカウントする。
875                                         {
876                                                 var chip = this.譜面.SSTFormatScore.チップリスト[ j ];
877
878                                                 // (A) 変更対象の小節内のチップ → 移動なし。カウント変更あり。小節はみ出しチェックあり。
879                                                 if( chip.小節番号 == i )
880                                                 {
881                                                         #region " 小節からはみ出したチップは、削除する。"
882                                                         //-----------------
883                                                         int 次小節の先頭位置grid = this.譜面.小節先頭の譜面内絶対位置gridを返す( i ) + (int) ( 変更後倍率 * this.GRID_PER_PART );
884                                                         if( 次小節の先頭位置grid <= chip.譜面内絶対位置grid )
885                                                         {
886                                                                 var chip変更前 = new SSTFormat.チップ( chip );
887
888                                                                 var cc = new UndoRedo.セル<SSTFormat.チップ>(
889                                                                         所有者ID: null,
890                                                                         Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
891                                                                                 変更対象.CopyFrom( 変更前 );
892                                                                                 this.譜面.SSTFormatScore.チップリスト.Add( 変更対象 );
893                                                                                 this.譜面.SSTFormatScore.チップリスト.Sort();
894                                                                                 this.未保存である = true;
895                                                                         },
896                                                                         Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
897                                                                                 this.譜面.SSTFormatScore.チップリスト.Remove( 変更対象 );
898                                                                                 this.未保存である = true;
899                                                                         },
900                                                                         変更対象: chip,
901                                                                         変更前の値: chip変更前,
902                                                                         変更後の値: null,
903                                                                         任意1: null,
904                                                                         任意2: null );
905
906                                                                 this.UndoRedo管理.セルを追加する( cc );
907                                                                 cc.Redoを実行する();
908                                                         }
909                                                         //-----------------
910                                                         #endregion
911                                                         #region " 小節からはみ出さなかったチップは、カウント(小節解像度と小節内位置)を変更する。"
912                                                         //-----------------
913                                                         else
914                                                         {
915                                                                 // 解像度が小さいと計算結果がおかしくなるので、無条件に解像度を上げる。
916
917                                                                 int 十分に大きな値 = this.GRID_PER_PART;   // 充分に大きい数なら何でもいい。
918                                                                 if( 十分に大きな値 > chip.小節解像度 )  // 何度も乗算されたら値が肥大化しまくるので、制限をかける。
919                                                                 {
920                                                                         chip.小節解像度 *= 十分に大きな値;
921                                                                         chip.小節内位置 *= 十分に大きな値;
922                                                                 }
923
924                                                                 // 小節解像度を変更する。(小節内位置は変更しない。)
925                                                                 chip.小節解像度 = (int) ( 変更後倍率 * chip.小節解像度 );
926                                                         }
927                                                         //-----------------
928                                                         #endregion
929                                                 }
930
931                                                 // (B) 変更対象より先の小節内のチップ → 移動あり。カウントなし。小節はみ出しチェックなし。
932                                                 else if( i < chip.小節番号 )
933                                                 {
934                                                         #region " チップを n変化量grid 移動する。"
935                                                         //-----------------
936                                                         var cc = new UndoRedo.セル<SSTFormat.チップ>(
937                                                                 所有者ID: null,
938                                                                 Undoアクション: ( 変更対象, 変更前, 変更後, _変化量grid, 任意2 ) => {
939                                                                         変更対象.譜面内絶対位置grid -= (int) _変化量grid;
940                                                                         this.未保存である = true;
941                                                                 },
942                                                                 Redoアクション: ( 変更対象, 変更前, 変更後, _変化量grid, 任意2 ) => {
943                                                                         変更対象.譜面内絶対位置grid += (int) _変化量grid;
944                                                                         this.未保存である = true;
945                                                                 },
946                                                                 変更対象: chip,
947                                                                 変更前の値: null,
948                                                                 変更後の値: null,
949                                                                 任意1: 変化量grid,
950                                                                 任意2: null );
951
952                                                         this.UndoRedo管理.セルを追加する( cc );
953                                                         cc.Redoを実行する();
954                                                         //-----------------
955                                                         #endregion
956                                                 }
957                                         }
958                                         //-----------------
959                                         #endregion
960                                 }
961                         }
962                         finally
963                         {
964                                 this.UndoRedo管理.トランザクション記録を終了する();
965
966                                 // 画面を再描画する。
967                                 this.UndoRedo用GUIのEnabledを設定する();
968                                 this.譜面をリフレッシュする();
969                         }
970                 }
971                 protected void Act小節を挿入する( int 挿入前小節番号 )
972                 {
973                         // 挿入する新しい小節の小節長は、直前の(挿入前小節番号-1 の小節)と同じサイズとする。
974                         double 小節長倍率 = ( 0 < 挿入前小節番号 ) ? this.譜面.SSTFormatScore.小節長倍率を取得する( 挿入前小節番号 - 1 ) : 1.0;
975
976                         // 移動する。
977                         try
978                         {
979                                 this.UndoRedo管理.トランザクション記録を開始する();
980
981                                 #region " 後方のチップを移動する。"
982                                 //-----------------
983                                 int 挿入に伴う増加量grid = (int) ( this.GRID_PER_PART * 小節長倍率 );
984
985                                 foreach( var chip in this.譜面.SSTFormatScore.チップリスト )
986                                 {
987                                         if( 挿入前小節番号 <= chip.小節番号 )
988                                         {
989                                                 var cell = new UndoRedo.セル<SSTFormat.チップ>(
990                                                         所有者ID: null,
991                                                         Undoアクション: ( 変更対象, 変更前, 変更後, _挿入に伴う増加量grid, 任意2 ) => {
992                                                                 変更対象.小節番号--;
993                                                                 変更対象.譜面内絶対位置grid -= (int) _挿入に伴う増加量grid;
994                                                         },
995                                                         Redoアクション: ( 変更対象, 変更前, 変更後, _挿入に伴う増加量grid, 任意2 ) => {
996                                                                 変更対象.小節番号++;
997                                                                 変更対象.譜面内絶対位置grid += (int) _挿入に伴う増加量grid;
998                                                         },
999                                                         変更対象: chip,
1000                                                         変更前の値: null,
1001                                                         変更後の値: null,
1002                                                         任意1: 挿入に伴う増加量grid,
1003                                                         任意2: null );
1004
1005                                                 this.UndoRedo管理.セルを追加する( cell );
1006                                                 cell.Redoを実行する();
1007                                         }
1008                                 }
1009                                 //-----------------
1010                                 #endregion
1011                                 #region " 後方の小節長倍率を移動する。"
1012                                 //-----------------
1013                                 var cc = new UndoRedo.セル<double>(
1014                                         所有者ID: null,
1015                                         Undoアクション: ( 変更対象, 変更前, 変更後, _挿入前小節番号, 任意2 ) => {
1016                                                 this.譜面.SSTFormatScore.小節長倍率リスト.RemoveAt( (int) _挿入前小節番号 );
1017                                                 this.未保存である = true;
1018                                         },
1019                                         Redoアクション: ( 変更対象, 変更前, 変更後, _挿入前小節番号, 任意2 ) => {
1020                                                 this.譜面.SSTFormatScore.小節長倍率リスト.Insert( (int) _挿入前小節番号, 小節長倍率 );
1021                                                 this.未保存である = true;
1022                                         },
1023                                         変更対象: 0.0,
1024                                         変更前の値: 0.0,
1025                                         変更後の値: 小節長倍率,
1026                                         任意1: 挿入前小節番号,
1027                                         任意2: null );
1028
1029                                 this.UndoRedo管理.セルを追加する( cc );
1030                                 cc.Redoを実行する();
1031                                 //-----------------
1032                                 #endregion
1033                         }
1034                         finally
1035                         {
1036                                 this.UndoRedo管理.トランザクション記録を終了する();
1037
1038                                 // 画面を再描画する。
1039                                 this.UndoRedo用GUIのEnabledを設定する();
1040                                 this.譜面をリフレッシュする();
1041                                 this.未保存である = true;
1042                         }
1043                 }
1044                 protected void Act小節を削除する( int 削除する小節番号 )
1045                 {
1046                         double 削除する小節の小節長倍率 = this.譜面.SSTFormatScore.小節長倍率を取得する( 削除する小節番号 );
1047
1048                         // 削除する。
1049                         try
1050                         {
1051                                 this.UndoRedo管理.トランザクション記録を開始する();
1052
1053                                 #region " 削除される小節内のチップをすべて削除する。"
1054                                 //-----------------
1055                                 for( int i = this.譜面.SSTFormatScore.チップリスト.Count - 1; i >= 0; i-- )
1056                                 {
1057                                         var chip = this.譜面.SSTFormatScore.チップリスト[ i ];
1058
1059                                         if( 削除する小節番号 == chip.小節番号 )
1060                                         {
1061                                                 var chip変更前 = new SSTFormat.チップ( chip );
1062
1063                                                 var cell = new UndoRedo.セル<SSTFormat.チップ>(
1064                                                         所有者ID: null,
1065                                                         Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
1066                                                                 変更対象.CopyFrom( 変更前 );
1067                                                                 this.譜面.SSTFormatScore.チップリスト.Add( 変更対象 );
1068                                                                 this.譜面.SSTFormatScore.チップリスト.Sort();
1069                                                         },
1070                                                         Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
1071                                                                 this.譜面.SSTFormatScore.チップリスト.Remove( 変更対象 );
1072                                                                 this.未保存である = true;
1073                                                         },
1074                                                         変更対象: chip,
1075                                                         変更前の値: chip変更前,
1076                                                         変更後の値: null,
1077                                                         任意1: null,
1078                                                         任意2: null );
1079
1080                                                 this.UndoRedo管理.セルを追加する( cell );
1081                                                 cell.Redoを実行する();
1082                                         }
1083                                 }
1084                                 //-----------------
1085                                 #endregion
1086                                 #region " 後方のチップを移動する。"
1087                                 //-----------------
1088                                 int 削除に伴う減少量grid = (int) ( this.GRID_PER_PART * 削除する小節の小節長倍率 );
1089
1090                                 foreach( var chip in this.譜面.SSTFormatScore.チップリスト )
1091                                 {
1092                                         if( 削除する小節番号 < chip.小節番号 )
1093                                         {
1094                                                 var cell = new UndoRedo.セル<SSTFormat.チップ>(
1095                                                         所有者ID: null,
1096                                                         Undoアクション: ( 変更対象, 変更前, 変更後, _削除に伴う減少量grid, 任意2 ) => {
1097                                                                 変更対象.小節番号++;
1098                                                                 変更対象.譜面内絶対位置grid += (int) _削除に伴う減少量grid;
1099                                                         },
1100                                                         Redoアクション: ( 変更対象, 変更前, 変更後, _削除に伴う減少量grid, 任意2 ) => {
1101                                                                 変更対象.小節番号--;
1102                                                                 変更対象.譜面内絶対位置grid -= (int) _削除に伴う減少量grid;
1103                                                         },
1104                                                         変更対象: chip,
1105                                                         変更前の値: null,
1106                                                         変更後の値: null,
1107                                                         任意1: 削除に伴う減少量grid,
1108                                                         任意2: null );
1109
1110                                                 this.UndoRedo管理.セルを追加する( cell );
1111                                                 cell.Redoを実行する();
1112                                         }
1113                                 }
1114                                 //-----------------
1115                                 #endregion
1116                                 #region " 後方の小節長倍率を移動する。"
1117                                 //-----------------
1118                                 var cc = new UndoRedo.セル<double>(
1119                                         所有者ID: null,
1120                                         Undoアクション: ( 変更対象, 変更前, 変更後, _削除する小節番号, 任意2 ) => {
1121                                                 this.譜面.SSTFormatScore.小節長倍率リスト.Insert( (int) _削除する小節番号, 変更前 );
1122                                                 this.未保存である = true;
1123                                         },
1124                                         Redoアクション: ( 変更対象, 変更前, 変更後, _削除する小節番号, 任意2 ) => {
1125                                                 this.譜面.SSTFormatScore.小節長倍率リスト.RemoveAt( (int) _削除する小節番号 );
1126                                                 this.未保存である = true;
1127                                         },
1128                                         変更対象: 0.0,
1129                                         変更前の値: 削除する小節の小節長倍率,
1130                                         変更後の値: 0.0,
1131                                         任意1: 削除する小節番号,
1132                                         任意2: null );
1133
1134                                 this.UndoRedo管理.セルを追加する( cc );
1135                                 cc.Redoを実行する();
1136                                 //-----------------
1137                                 #endregion
1138                         }
1139                         finally
1140                         {
1141                                 this.UndoRedo管理.トランザクション記録を終了する();
1142
1143                                 // 画面を再描画する。
1144                                 this.UndoRedo用GUIのEnabledを設定する();
1145                                 this.譜面をリフレッシュする();
1146                         }
1147                 }
1148                 protected void Act小節の先頭へ移動する( int 小節番号 )
1149                 {
1150                         // 小節番号をクリッピングする。
1151                         if( 0 > 小節番号 )
1152                                 小節番号 = 0;
1153                         else if( 小節番号 > this.譜面.SSTFormatScore.最大小節番号 )
1154                                 小節番号 = this.譜面.SSTFormatScore.最大小節番号;
1155
1156                         // 垂直スクロールバーを移動させると、画面も自動的に移動する。
1157                         var bar = this.vScrollBar譜面用垂直スクロールバー;
1158                         bar.Value = ( ( bar.Maximum + 1 ) - bar.LargeChange ) - this.譜面.小節先頭の譜面内絶対位置gridを返す( 小節番号 );
1159                 }
1160
1161                 // アクション補佐メソッド
1162
1163                 protected void エディタを初期化する()
1164                 {
1165                         this.編集中のファイル名 = null;
1166
1167                         #region " 各種オブジェクトを生成する。"
1168                         //-----------------
1169                         this.譜面?.Dispose();
1170                         this.譜面 = new C譜面( this );  // 譜面は、選択・編集モードよりも先に生成すること。
1171
1172                         this.UndoRedo管理 = new UndoRedo.管理();
1173                         this.選択モード = new 選択モード( this );
1174                         this.編集モード = new 編集モード( this );
1175                         this.クリップボード = new クリップボード( this );
1176                         //-----------------
1177                         #endregion
1178
1179                         // GUI の初期値を設定する。
1180
1181                         #region " 基本情報タブ "
1182                         //-----------------
1183                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
1184                         this.textBox曲名.Clear();
1185
1186                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
1187                         this.textBox説明.Clear();
1188                         //-----------------
1189                         #endregion
1190                         #region " Viewer 再生 "
1191                         //-----------------
1192                         this.Viewer再生関連GUIのEnabledを設定する();
1193                         //-----------------
1194                         #endregion
1195                         #region " ガイド間隔 "
1196                         //-----------------
1197                         this.Actガイド間隔を変更する( 16 );   // 初期値は 1/16。
1198                         //-----------------
1199                         #endregion
1200                         #region " 譜面拡大率 "
1201                         //-----------------
1202                         this.Act譜面拡大率を変更する( 1 );            // 初期値は x1.0。
1203                         //-----------------
1204                         #endregion
1205                         #region " Undo/Redo "
1206                         //-----------------
1207                         this.次のプロパティ変更がUndoRedoリストに載るようにする();
1208                         this.UndoRedo用GUIのEnabledを設定する();
1209                         //-----------------
1210                         #endregion
1211                         #region " 垂直スクロールバー "
1212                         //-----------------
1213                         this.vScrollBar譜面用垂直スクロールバー.Minimum = 0;
1214                         this.vScrollBar譜面用垂直スクロールバー.Maximum = ( this.譜面.全小節の高さgrid - 1 );
1215                         this.vScrollBar譜面用垂直スクロールバー.SmallChange = ( this.GRID_PER_PART / 16 );
1216                         this.vScrollBar譜面用垂直スクロールバー.LargeChange = this.GRID_PER_PART;
1217                         this.vScrollBar譜面用垂直スクロールバー.Value = this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
1218                         //-----------------
1219                         #endregion
1220
1221                         // 最初は編集モードで始める。
1222
1223                         this.編集モードに切替えて関連GUIを設定する();
1224                         this.未保存である = false;
1225                 }
1226                 protected DialogResult 未保存なら保存する()
1227                 {
1228                         #region " 既に保存済みなら何もしない。"
1229                         //-----------------
1230                         if( false == this.未保存である )
1231                                 return DialogResult.OK;
1232                         //-----------------
1233                         #endregion
1234                         #region "[編集中のデータを保存しますか?] ダイアログを表示し、回答を待つ。"
1235                         //-----------------
1236                         var result = MessageBox.Show(
1237                                 Properties.Resources.MSG_編集中のデータを保存しますか,
1238                                 Properties.Resources.MSG_確認ダイアログのタイトル,
1239                                 MessageBoxButtons.YesNoCancel,
1240                                 MessageBoxIcon.Question,
1241                                 MessageBoxDefaultButton.Button1 );
1242                         //-----------------
1243                         #endregion
1244                         #region " YES なら上書き保存する。"
1245                         //-----------------
1246                         if( DialogResult.Yes == result )
1247                                 this.Act上書き保存する();
1248                         //-----------------
1249                         #endregion
1250                         #region " 画面を再描画してダイアログを消去する。"
1251                         //-----------------
1252                         this.Refresh();
1253                         //-----------------
1254                         #endregion
1255
1256                         return result;  // ダイアログの結果を返す。
1257                 }
1258                 protected string ファイル保存ダイアログを開いてファイル名を取得する()
1259                 {
1260                         DialogResult result;
1261                         string ファイル名;
1262
1263                         // ダイアログでファイル名を取得する。
1264                         using( var dialog = new SaveFileDialog() {
1265                                 Title = "名前をつけて保存",
1266                                 Filter = "SSTFファイル(*.sstf)|*.sstf",
1267                                 FilterIndex = 1,
1268                                 InitialDirectory = this.作業フォルダパス,
1269                         } )
1270                         {
1271                                 result = dialog.ShowDialog( this );
1272                                 ファイル名 = dialog.FileName;
1273                         }
1274
1275                         // 画面を再描画してダイアログのゴミを消去する。
1276                         this.Refresh();
1277
1278                         // キャンセルされたら ""(空文字列)を返す。
1279                         if( DialogResult.OK != result )
1280                                 return "";
1281
1282                         // ファイルの拡張子を .sstf に変更。
1283                         if( 0 == Path.GetExtension( ファイル名 ).Length )
1284                                 ファイル名 = Path.ChangeExtension( ファイル名, ".sstf" );
1285
1286                         return ファイル名;
1287                 }
1288                 protected void ファイルを読み込む( string ファイル名 )
1289                 {
1290                         this.エディタを初期化する();
1291
1292                         #region " [読み込み中です] ポップアップを表示する。"
1293                         //-----------------
1294                         var msg = new Popupメッセージ( 
1295                                 Properties.Resources.MSG_読み込み中です + Environment.NewLine + 
1296                                 Properties.Resources.MSG_しばらくお待ち下さい );
1297                         msg.Owner = this;
1298                         msg.Show();
1299                         msg.Refresh();
1300                         //-----------------
1301                         #endregion
1302
1303                         try
1304                         {
1305                                 this.譜面.曲データファイルを読み込む( ファイル名 );
1306
1307                                 // 最低でも 10 小節は存在させる。
1308                                 for( int n = this.譜面.SSTFormatScore.最大小節番号 + 1; n < 9; n++ )
1309                                         ;
1310
1311                                 string 読み込み時の拡張子 = Path.GetExtension( ファイル名 ).ToLower();
1312                                 this.編集中のファイル名 = Path.ChangeExtension( Path.GetFileName( ファイル名 ), ".sstf" );                // 読み込んだファイルの拡張子を .sstf に変換。
1313                                 this.作業フォルダパス = Path.GetDirectoryName( ファイル名 );
1314
1315                                 // 読み込んだファイルを [ファイル]メニューの最近使ったファイル一覧に追加する。
1316                                 this.Config.ファイルを最近使ったファイルの一覧に追加する( Path.Combine( this.作業フォルダパス, this.編集中のファイル名 ) );
1317                                 this.ConfigのRecentUsedFilesをファイルメニューへ追加する();
1318
1319                                 // 基本情報タブを設定する。
1320                                 譜面.SSTFormatScore.背景動画ファイル名 =
1321                                         ( from file in Directory.GetFiles( Path.GetDirectoryName( this.作業フォルダパス ) )
1322                                           where SSTFormat.スコア.背景動画のデフォルト拡張子s.Any( 拡張子名 => ( Path.GetExtension( file ).ToLower() == 拡張子名 ) )
1323                                           select file ).FirstOrDefault();  // 複数あったら、最初に見つけたほうを採用。1つも見つからなければ null。
1324                                 this.次のプロパティ変更がUndoRedoリストに載らないようにする();
1325                                 this.textBox曲名.Text = 譜面.SSTFormatScore.Header.曲名;
1326                                 this.次のプロパティ変更がUndoRedoリストに載らないようにする();
1327                                 this.textBox説明.Text = 譜面.SSTFormatScore.Header.説明文;
1328                                 this.次のプロパティ変更がUndoRedoリストに載らないようにする();
1329                                 this.textBox背景動画.Text = Path.GetFileName( 譜面.SSTFormatScore.背景動画ファイル名 );
1330                                 this.次のプロパティ変更がUndoRedoリストに載らないようにする();
1331                                 this.textBoxメモ.Text = ( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 0 ) ) ? this.譜面.SSTFormatScore.dicメモ[ 0 ] : "";
1332
1333                                 // ウィンドウのタイトルバーの表示変更(str編集中のファイル名 が確定した後に)
1334                                 this.未保存である = true;     // 以前の状態によらず、確実に更新するようにする。
1335
1336                                 // sstf 以外を読み込んだ場合は、未保存状態のままとする。
1337                                 if( 読み込み時の拡張子.ToLower() == ".sstf" )
1338                                         this.未保存である = false;
1339                         }
1340                         catch( InvalidDataException )
1341                         {
1342                                 MessageBox.Show(
1343                                         Properties.Resources.MSG_対応していないファイルです,
1344                                         Properties.Resources.MSG_確認ダイアログのタイトル,
1345                                         MessageBoxButtons.OK );
1346                         }
1347
1348                         #region "「読み込み中です」ポップアップを閉じる。 "
1349                         //-----------------
1350                         msg.Close();
1351                         //-----------------
1352                         #endregion
1353
1354                         // 最後に、ダイアログのゴミなどを消すために画面を再描画する。
1355                         this.Refresh();
1356                 }
1357                 protected enum タブ種別 : int { 基本情報 = 0 }
1358                 protected void タブを選択する( タブ種別 eタブ種別 )
1359                 {
1360                         this.tabControl情報タブコンテナ.SelectedIndex = (int) eタブ種別;
1361                 }
1362                 protected void 次のプロパティ変更がUndoRedoリストに載らないようにする()
1363                 {
1364                         UndoRedo.管理.UndoRedoした直後である = true;
1365                 }
1366                 protected void 次のプロパティ変更がUndoRedoリストに載るようにする()
1367                 {
1368                         UndoRedo.管理.UndoRedoした直後である = false;
1369                 }
1370                 protected void 垂直スクロールバーと譜面の上下位置を調整する()
1371                 {
1372                         var bar = this.vScrollBar譜面用垂直スクロールバー;
1373                         var box = this.pictureBox譜面パネル;
1374                         var panel2 = this.splitContainer分割パネルコンテナ.Panel2;
1375                 
1376                         // 譜面パネルの長さをパネルに合わせて調整する。
1377                         box.ClientSize = new Size(
1378                                 box.ClientSize.Width, 
1379                                 panel2.ClientSize.Height - box.Location.Y );
1380
1381                         // 現在のバーの位置を割合で記憶する。
1382                         var bar率 = (double) bar.Value / (double) ( bar.Maximum - bar.Minimum );
1383
1384                         // 新しい値域を設定した後、バーの位置を記憶しておいた割合で設定。
1385                         bar.Minimum = 0;
1386                         bar.Maximum = this.譜面.全小節の高さgrid - 1;
1387                         bar.Value = (int) ( ( bar.Maximum - bar.Minimum ) * bar率 );
1388
1389                         // 譜面長さが画面長さより短いなら、スクロールバーを表示しない。
1390                         bar.Enabled = ( bar.Maximum > bar.LargeChange ) ? true : false;
1391                 }
1392                 protected void Viewer再生関連GUIのEnabledを設定する()
1393                 {
1394                         if( File.Exists( this.Config.ViewerPath ) )
1395                         {
1396                                 // (A) Viewer が存在するなら、各GUIは有効。
1397
1398                                 this.toolStripButton先頭から再生.Enabled = true;
1399                                 this.toolStripButton現在位置から再生.Enabled = true;
1400                                 this.toolStripButton現在位置からBGMのみ再生.Enabled = true;
1401                                 this.toolStripButton再生停止.Enabled = true;
1402
1403                                 this.toolStripMenuItem先頭から再生.Enabled = true;
1404                                 this.toolStripMenuItem現在位置から再生.Enabled = true;
1405                                 this.toolStripMenuItem現在位置からBGMのみ再生.Enabled = true;
1406                                 this.toolStripMenuItem再生停止.Enabled = true;
1407                         }
1408                         else
1409                         {
1410                                 // (B) Viewer が存在しないなら、各GUIは無効。
1411
1412                                 this.toolStripButton先頭から再生.Enabled = false;
1413                                 this.toolStripButton現在位置から再生.Enabled = false;
1414                                 this.toolStripButton現在位置からBGMのみ再生.Enabled = false;
1415                                 this.toolStripButton再生停止.Enabled = false;
1416
1417                                 this.toolStripMenuItem先頭から再生.Enabled = false;
1418                                 this.toolStripMenuItem現在位置から再生.Enabled = false;
1419                                 this.toolStripMenuItem現在位置からBGMのみ再生.Enabled = false;
1420                                 this.toolStripMenuItem再生停止.Enabled = false;
1421                         }
1422                 }
1423                 protected void ConfigのRecentUsedFilesをファイルメニューへ追加する()
1424                 {
1425                         #region " [ファイル] メニューから、[最近使ったファイルの一覧] をクリアする。"
1426                         //-----------------
1427                         for( int i = 0; i < this.toolStripMenuItemファイル.DropDownItems.Count; i++ )
1428                         {
1429                                 var item = this.toolStripMenuItemファイル.DropDownItems[ i ];
1430
1431                                 // ↓削除したくないサブメニューの一覧。これ以外のサブメニュー項目はすべて削除する。
1432                                 if( item != this.toolStripMenuItem新規作成 &&
1433                                         item != this.toolStripMenuItem開く &&
1434                                         item != this.toolStripMenuItem上書き保存 &&
1435                                         item != this.toolStripMenuItem名前を付けて保存 &&
1436                                         item != this.toolStripSeparator1 &&
1437                                         item != this.toolStripMenuItem終了 )
1438                                 {
1439                                         this.toolStripMenuItemファイル.DropDownItems.Remove( item );
1440                                         i = -1; // 要素数が変わったので列挙しなおし。RemoveAll() はないのか。
1441                                 }
1442                         }
1443                         //-----------------
1444                         #endregion
1445
1446                         if( ( false == this.Config.ShowRecentUsedFiles ) ||     // 表示しない or
1447                                 ( 0 == this.Config.RecentUsedFiles.Count ) )    // 履歴が 0 件
1448                                 return;
1449
1450                         #region " Config が持つ履歴にそって、[ファイル] メニューにサブメニュー項目リストを追加する(ただし最大表示数まで)。"
1451                         //-----------------
1452                         // [File] のサブメニューリストに項目が1つでもある場合は、履歴サブメニュー項目を追加する前に、[終了] の下にセパレータを入れる。手動で。
1453                         bool セパレータの追加がまだ = true;
1454
1455                         // すべての [最近使ったファイル] について...
1456                         for( int i = 0; i < this.Config.RecentUsedFiles.Count; i++ )
1457                         {
1458                                 // 最大表示数を越えたら中断。
1459                                 if( this.Config.MaxOfUsedRecentFiles <= i )
1460                                         return;
1461
1462                                 // ファイルパスを、サブメニュー項目として [ファイル] メニューに追加する。
1463                                 string ファイルパス = this.Config.RecentUsedFiles[ i ];
1464                                 if( string.IsNullOrEmpty( ファイルパス ) )
1465                                         continue;
1466
1467                                 // セパレータの追加がまだなら追加する。
1468                                 if( セパレータの追加がまだ )
1469                                 {
1470                                         this.toolStripMenuItemファイル.DropDownItems.Add( new ToolStripSeparator() { Size = this.toolStripSeparator1.Size } );
1471                                         セパレータの追加がまだ = false;
1472                                 }
1473
1474                                 // ToolStripMenuItem を手動で作って [ファイル] のサブメニューリストに追加する。
1475                                 var item = new ToolStripMenuItem() {
1476                                         Name = "最近使ったファイル" + i,
1477                                         Size = this.toolStripMenuItem終了.Size,
1478                                         Text = "&" + i + " " + ファイルパス,
1479                                 };
1480                                 item.Click += new EventHandler( this.toolStripMenuItem最近使ったファイル_Click );
1481                                 this.toolStripMenuItemファイル.DropDownItems.Add( item );
1482
1483                                 // 追加したファイルが既に存在していないなら項目を無効化(グレー表示)する。
1484                                 if( false == File.Exists( ファイルパス ) )
1485                                         item.Enabled = false;
1486                         }
1487                         //-----------------
1488                         #endregion
1489                 }
1490                 protected Point 現在のマウス位置を譜面パネル内座標pxに変換して返す()
1491                 {
1492                         return this.pictureBox譜面パネル.PointToClient( new Point( Cursor.Position.X, Cursor.Position.Y ) );
1493                 }
1494                 protected void 現在のチップ音量をツールバーに表示する()
1495                 {
1496                         this.toolStripLabel音量.Text = ( this.dic音量ラベル.ContainsKey( this.現在のチップ音量 ) ) ? this.dic音量ラベル[ this.現在のチップ音量 ] : @"???";
1497                 }
1498
1499                 // GUIイベントメソッド
1500
1501                 #region " メインフォーム "
1502                 //-----------------
1503                 protected void メインフォーム_DragDrop( object sender, DragEventArgs e )
1504                 {
1505                         string[] data = (string[]) e.Data.GetData( DataFormats.FileDrop );
1506
1507                         if( 1 <= data.Length )
1508                                 this.Act指定されたファイルを開く( data[ 0 ] );                      // Dropされたファイルが複数個あっても、先頭のファイルだけを有効とする。
1509                 }
1510                 protected void メインフォーム_DragEnter( object sender, DragEventArgs e )
1511                 {
1512                         if( e.Data.GetDataPresent( DataFormats.FileDrop ) )
1513                         {
1514                                 e.Effect = DragDropEffects.Copy;        // ファイルならコピーと見なす(カーソルがコピー型になる)
1515                         }
1516                         else
1517                         {
1518                                 e.Effect = DragDropEffects.None;        // ファイルじゃないなら無視(カーソル変化なし)
1519                         }
1520                 }
1521                 protected void メインフォーム_FormClosing( object sender, FormClosingEventArgs e )
1522                 {
1523                         if( DialogResult.Cancel == this.未保存なら保存する() )
1524                         {
1525                                 e.Cancel = true;
1526                         }
1527                         else
1528                         {
1529                                 this.Actアプリを終了する();
1530                         }
1531                 }
1532                 //-----------------
1533                 #endregion
1534                 #region " メニューバー "
1535                 //-----------------
1536                 // File
1537                 protected void toolStripMenuItem新規作成_Click( object sender, EventArgs e )
1538                 {
1539                         this.Act新規作成する();
1540                 }
1541                 protected void toolStripMenuItem開く_Click( object sender, EventArgs e )
1542                 {
1543                         this.Act開く();
1544                 }
1545                 protected void toolStripMenuItem上書き保存_Click( object sender, EventArgs e )
1546                 {
1547                         this.Act上書き保存する();
1548                 }
1549                 protected void toolStripMenuItem名前を付けて保存_Click( object sender, EventArgs e )
1550                 {
1551                         this.Act名前を付けて保存する();
1552                 }
1553                 protected void toolStripMenuItem終了_Click( object sender, EventArgs e )
1554                 {
1555                         this.Act終了する();
1556                 }
1557                 protected void toolStripMenuItem最近使ったファイル_Click( object sender, EventArgs e )
1558                 {
1559                         // ※このイベントハンドラに対応する「toolStripMenuItem最近使ったファイル」というアイテムはデザイナにはないので注意。
1560                         //   「this.t最近使ったファイルをFileメニューへ追加する()」の中で、手動で作って追加したアイテムに対するハンドラである。
1561                         
1562                         this.Act指定されたファイルを開く( ( (ToolStripMenuItem) sender ).Text.Substring( 3 ) );
1563                 }
1564
1565                 // Edit
1566                 protected void toolStripMenuItem元に戻す_Click( object sender, EventArgs e )
1567                 {
1568                         this.Act元に戻す();
1569                 }
1570                 protected void toolStripMenuItemやり直す_Click( object sender, EventArgs e )
1571                 {
1572                         this.Actやり直す();
1573                 }
1574                 protected void toolStripMenuItem切り取り_Click( object sender, EventArgs e )
1575                 {
1576                         this.Act切り取る();
1577                 }
1578                 protected void toolStripMenuItemコピー_Click( object sender, EventArgs e )
1579                 {
1580                         this.Actコピーする();
1581                 }
1582                 protected void toolStripMenuItem貼り付け_Click( object sender, EventArgs e )
1583                 {
1584                         var マウスの位置 = this.現在のマウス位置を譜面パネル内座標pxに変換して返す();
1585
1586                         // (A) マウスが譜面上になかった → 表示領域下辺から貼り付ける。
1587                         if( ( ( 0 > マウスの位置.X ) || ( 0 > マウスの位置.Y ) ) ||
1588                                 ( ( マウスの位置.X > this.譜面パネルサイズ.Width ) || ( マウスの位置.Y > this.譜面パネルサイズ.Height ) ) )
1589                         {
1590                                 this.Act貼り付ける( this.譜面.現在の譜面表示下辺の譜面内絶対位置grid );
1591                         }
1592                         // (B) マウスが譜面上にある → そこから貼り付ける。
1593                         else
1594                         {
1595                                 this.Act貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
1596                         }
1597                 }
1598                 protected void toolStripMenuItem削除_Click( object sender, EventArgs e )
1599                 {
1600                         this.Act削除する();
1601                 }
1602                 protected void toolStripMenuItemすべて選択_Click( object sender, EventArgs e )
1603                 {
1604                         this.Actすべて選択する();
1605                 }
1606                 protected void toolStripMenuItem選択モード_Click( object sender, EventArgs e )
1607                 {
1608                         this.選択モードに切替えて関連GUIを設定する();
1609                 }
1610                 protected void toolStripMenuItem編集モード_Click( object sender, EventArgs e )
1611                 {
1612                         this.編集モードに切替えて関連GUIを設定する();
1613                 }
1614                 protected void toolStripMenuItemモード切替え_Click( object sender, EventArgs e )
1615                 {
1616                         if( this.選択モードである )
1617                         {
1618                                 this.編集モードに切替えて関連GUIを設定する();
1619                         }
1620                         else
1621                         {
1622                                 this.選択モードに切替えて関連GUIを設定する();
1623                         }
1624                 }
1625                 protected void toolStripMenuItem検索_Click( object sender, EventArgs e )
1626                 {
1627                         this.Act検索する();
1628                 }
1629
1630                 // View
1631                 protected void toolStripMenuItemガイド間隔4分_Click( object sender, EventArgs e )
1632                 {
1633                         this.Actガイド間隔を変更する( 4 );
1634                 }
1635                 protected void toolStripMenuItemガイド間隔6分_Click( object sender, EventArgs e )
1636                 {
1637                         this.Actガイド間隔を変更する( 6 );
1638                 }
1639                 protected void toolStripMenuItemガイド間隔8分_Click( object sender, EventArgs e )
1640                 {
1641                         this.Actガイド間隔を変更する( 8 );
1642                 }
1643                 protected void toolStripMenuItemガイド間隔12分_Click( object sender, EventArgs e )
1644                 {
1645                         this.Actガイド間隔を変更する( 12 );
1646                 }
1647                 protected void toolStripMenuItemガイド間隔16分_Click( object sender, EventArgs e )
1648                 {
1649                         this.Actガイド間隔を変更する( 16 );
1650                 }
1651                 protected void toolStripMenuItemガイド間隔24分_Click( object sender, EventArgs e )
1652                 {
1653                         this.Actガイド間隔を変更する( 24 );
1654                 }
1655                 protected void toolStripMenuItemガイド間隔32分_Click( object sender, EventArgs e )
1656                 {
1657                         this.Actガイド間隔を変更する( 32 );
1658                 }
1659                 protected void toolStripMenuItemガイド間隔48分_Click( object sender, EventArgs e )
1660                 {
1661                         this.Actガイド間隔を変更する( 48 );
1662                 }
1663                 protected void toolStripMenuItemガイド間隔64分_Click( object sender, EventArgs e )
1664                 {
1665                         this.Actガイド間隔を変更する( 64 );
1666                 }
1667                 protected void toolStripMenuItemガイド間隔128分_Click( object sender, EventArgs e )
1668                 {
1669                         this.Actガイド間隔を変更する( 128 );
1670                 }
1671                 protected void toolStripMenuItemガイド間隔フリー_Click( object sender, EventArgs e )
1672                 {
1673                         this.Actガイド間隔を変更する( 0 );
1674                 }
1675                 protected void toolStripMenuItemガイド間隔拡大_Click( object sender, EventArgs e )
1676                 {
1677                         this.Actガイド間隔を拡大する();
1678                 }
1679                 protected void toolStripMenuItemガイド間隔縮小_Click( object sender, EventArgs e )
1680                 {
1681                         this.Actガイド間隔を縮小する();
1682                 }
1683
1684                 // Play
1685                 protected void toolStripMenuItem先頭から再生_Click( object sender, EventArgs e )
1686                 {
1687                         this.Act最初から再生する();
1688                 }
1689                 protected void toolStripMenuItem現在位置から再生_Click( object sender, EventArgs e )
1690                 {
1691                         this.Act現在位置から再生する();
1692                 }
1693                 protected void toolStripMenuItem現在位置からBGMのみ再生_Click( object sender, EventArgs e )
1694                 {
1695                         this.Act現在位置からBGMのみ再生する();
1696                 }
1697                 protected void toolStripMenuItem再生停止_Click( object sender, EventArgs e )
1698                 {
1699                         this.Act再生を停止する();
1700                 }
1701
1702                 // Tool
1703                 protected void toolStripMenuItemオプション_Click( object sender, EventArgs e )
1704                 {
1705                         this.Actオプションを設定する();
1706                 }
1707
1708                 // Help
1709                 protected void toolStripMenuItemバージョン_Click( object sender, EventArgs e )
1710                 {
1711                         this.Actバージョンを表示する();
1712                 }
1713                 //-----------------
1714                 #endregion
1715                 #region " ツールバー "
1716                 //-----------------
1717                 protected void toolStripButton新規作成_Click( object sender, EventArgs e )
1718                 {
1719                         this.Act新規作成する();
1720                 }
1721                 protected void toolStripButton開く_Click( object sender, EventArgs e )
1722                 {
1723                         this.Act開く();
1724                 }
1725                 protected void toolStripButton上書き保存_Click( object sender, EventArgs e )
1726                 {
1727                         this.Act上書き保存する();
1728                 }
1729                 protected void toolStripButton切り取り_Click( object sender, EventArgs e )
1730                 {
1731                         this.Act切り取る();
1732                 }
1733                 protected void toolStripButtonコピー_Click( object sender, EventArgs e )
1734                 {
1735                         this.Actコピーする();
1736                 }
1737                 protected void toolStripButton貼り付け_Click( object sender, EventArgs e )
1738                 {
1739                         var マウスの位置 = this.現在のマウス位置を譜面パネル内座標pxに変換して返す();
1740
1741                         // (A) マウスが譜面上になかった → 表示領域下辺から貼り付ける。
1742                         if( ( ( マウスの位置.X < 0 ) || ( マウスの位置.Y < 0 ) ) ||
1743                                 ( ( マウスの位置.X > this.譜面パネルサイズ.Width ) || ( マウスの位置.Y > this.譜面パネルサイズ.Height ) ) )
1744                         {
1745                                 this.Act貼り付ける( this.譜面.現在の譜面表示下辺の譜面内絶対位置grid );
1746                         }
1747                         // (B) マウスが譜面上にある → そこから貼り付ける。
1748                         else
1749                         {
1750                                 this.Act貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
1751                         }
1752                 }
1753                 protected void toolStripButton削除_Click( object sender, EventArgs e )
1754                 {
1755                         this.Act削除する();
1756                 }
1757                 
1758                 protected void toolStripButton元に戻す_Click( object sender, EventArgs e )
1759                 {
1760                         this.Act元に戻す();
1761                 }
1762                 protected void toolStripButtonやり直す_Click( object sender, EventArgs e )
1763                 {
1764                         this.Actやり直す();
1765                 }
1766
1767                 protected void toolStripComboBox譜面拡大率_SelectedIndexChanged( object sender, EventArgs e )
1768                 {
1769                         this.Act譜面拡大率を変更する( this.toolStripComboBox譜面拡大率.SelectedIndex + 1 );
1770                 }
1771                 protected void toolStripComboBoxガイド間隔_SelectedIndexChanged( object sender, EventArgs e )
1772                 {
1773                         switch( this.toolStripComboBoxガイド間隔.SelectedIndex )
1774                         {
1775                                 case 0:
1776                                         this.Actガイド間隔を変更する( 4 );
1777                                         return;
1778
1779                                 case 1:
1780                                         this.Actガイド間隔を変更する( 6 );
1781                                         return;
1782
1783                                 case 2:
1784                                         this.Actガイド間隔を変更する( 8 );
1785                                         return;
1786
1787                                 case 3:
1788                                         this.Actガイド間隔を変更する( 12 );
1789                                         return;
1790
1791                                 case 4:
1792                                         this.Actガイド間隔を変更する( 16 );
1793                                         return;
1794
1795                                 case 5:
1796                                         this.Actガイド間隔を変更する( 24 );
1797                                         return;
1798
1799                                 case 6:
1800                                         this.Actガイド間隔を変更する( 32 );
1801                                         return;
1802
1803                                 case 7:
1804                                         this.Actガイド間隔を変更する( 48 );
1805                                         return;
1806
1807                                 case 8:
1808                                         this.Actガイド間隔を変更する( 64 );
1809                                         return;
1810
1811                                 case 9:
1812                                         this.Actガイド間隔を変更する( 128 );
1813                                         return;
1814
1815                                 case 10:
1816                                         this.Actガイド間隔を変更する( 0 );
1817                                         return;
1818                         }
1819
1820                 }
1821                 protected void toolStripButton選択モード_Click( object sender, EventArgs e )
1822                 {
1823                         this.Act選択モードにする();
1824                 }
1825                 protected void toolStripButton編集モード_Click( object sender, EventArgs e )
1826                 {
1827                         this.Act編集モードにする();
1828                 }
1829
1830                 protected void toolStripButton先頭から再生_Click( object sender, EventArgs e )
1831                 {
1832                         this.Act最初から再生する();
1833                 }
1834                 protected void toolStripButton現在位置から再生_Click( object sender, EventArgs e )
1835                 {
1836                         this.Act現在位置から再生する();
1837                 }
1838                 protected void toolStripButton現在位置からBGMのみ再生_Click( object sender, EventArgs e )
1839                 {
1840                         this.Act現在位置からBGMのみ再生する();
1841                 }
1842                 protected void toolStripButton再生停止_Click( object sender, EventArgs e )
1843                 {
1844                         this.Act再生を停止する();
1845                 }
1846
1847                 protected void toolStripButton音量Down_Click( object sender, EventArgs e )
1848                 {
1849                         int 新音量 = this.現在のチップ音量 - 1;
1850                         this.現在のチップ音量 = ( 新音量 < メインフォーム.最小音量 ) ? メインフォーム.最小音量 : 新音量;
1851
1852                         this.現在のチップ音量をツールバーに表示する();
1853                 }
1854                 protected void toolStripButton音量UP_Click( object sender, EventArgs e )
1855                 {
1856                         int 新音量 = this.現在のチップ音量 + 1;
1857                         this.現在のチップ音量 = ( 新音量 > メインフォーム.最大音量 ) ? メインフォーム.最大音量 : 新音量;
1858
1859                         this.現在のチップ音量をツールバーに表示する();
1860                 }
1861                 //-----------------
1862                 #endregion
1863                 #region " 分割パネルコンテナ、譜面パネル、スクロールバー "
1864                 //-----------------
1865                 protected void pictureBox譜面パネル_MouseClick( object sender, MouseEventArgs e )
1866                 {
1867                         // フォーカスを得る。
1868                         this.pictureBox譜面パネル.Focus();
1869
1870                         // 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
1871                         if( this.選択モードである )
1872                         {
1873                                 this.選択モード.MouseClick( e );
1874                         }
1875                         else
1876                         {
1877                                 this.編集モード.MouseClick( e );
1878                         }
1879                 }
1880                 protected void pictureBox譜面パネル_MouseDown( object sender, MouseEventArgs e )
1881                 {
1882                         // 選択モードオブジェクトへ処理を引き継ぐ。
1883                         if( this.選択モードである )
1884                                 this.選択モード.MouseDown( e );
1885                 }
1886                 protected void pictureBox譜面パネル_MouseEnter( object sender, EventArgs e )
1887                 {
1888                         // オートフォーカスが有効の場合、譜面にマウスが入ったら譜面がフォーカスを得る。"
1889                         if( this.Config.AutoFocus )
1890                                 this.pictureBox譜面パネル.Focus();
1891                 }
1892                 protected void pictureBox譜面パネル_MouseLeave( object sender, EventArgs e )
1893                 {
1894                         // 編集モードオブジェクトへ処理を引き継ぐ。
1895                         if( this.編集モードである )
1896                                 this.編集モード.MouseLeave( e );
1897                 }
1898                 protected void pictureBox譜面パネル_MouseMove( object sender, MouseEventArgs e )
1899                 {
1900                         // 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
1901                         if( this.選択モードである )
1902                         {
1903                                 this.選択モード.MouseMove( e );
1904                         }
1905                         else
1906                         {
1907                                 this.編集モード.MouseMove( e );
1908                         }
1909                 }
1910                 protected void pictureBox譜面パネル_Paint( object sender, PaintEventArgs e )
1911                 {
1912                         if( false == this.初期化完了 )
1913                                 return;         // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
1914
1915                         #region " 小節数が変わってたら、スクロールバーの値域を調整する。"
1916                         //-----------------
1917                         int 全譜面の高さgrid = this.譜面.全小節の高さgrid;
1918
1919                         if( this.vScrollBar譜面用垂直スクロールバー.Maximum != 全譜面の高さgrid - 1 ) // 小節数が変わっている
1920                         {
1921                                 // 譜面の高さ(grid)がどれだけ変わったか?
1922                                 int 増加分grid = ( 全譜面の高さgrid - 1 ) - this.vScrollBar譜面用垂直スクロールバー.Maximum;
1923
1924                                 #region " スクロールバーの状態を新しい譜面の高さに合わせる。"
1925                                 //-----------------
1926                                 {
1927                                         int value = this.vScrollBar譜面用垂直スクロールバー.Value;          // 次の式で Maximum が Value より小さくなると例外が発生するので、
1928                                         this.vScrollBar譜面用垂直スクロールバー.Value = 0;                          // Value のバックアップを取っておいて、ひとまず 0 にする。
1929                                         this.vScrollBar譜面用垂直スクロールバー.Maximum = 全譜面の高さgrid - 1;
1930
1931                                         int newValue = value + 増加分grid;
1932
1933                                         // オーバーフローしないようクリッピングする。
1934                                         if( 0 > newValue )
1935                                         {
1936                                                 this.vScrollBar譜面用垂直スクロールバー.Value = 0;
1937                                         }
1938                                         else if( ( this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange ) <= newValue )
1939                                         {
1940                                                 this.vScrollBar譜面用垂直スクロールバー.Value = this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
1941                                         }
1942                                         else
1943                                         {
1944                                                 this.vScrollBar譜面用垂直スクロールバー.Value = newValue;
1945                                         }
1946                                 }
1947                                 //-----------------
1948                                 #endregion
1949                                 #region " 譜面表示下辺の位置を更新する。"
1950                                 //-----------------
1951                                 this.譜面.現在の譜面表示下辺の譜面内絶対位置grid =
1952                                         ( ( this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange ) + 1 ) - this.vScrollBar譜面用垂直スクロールバー.Value;
1953                                 //-----------------
1954                                 #endregion
1955                         }
1956                         //-----------------
1957                         #endregion
1958                         #region " 譜面を描画する。"
1959                         //-----------------
1960                         this.譜面.描画する( e.Graphics, this.pictureBox譜面パネル );
1961                         //-----------------
1962                         #endregion
1963                         
1964                         // 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
1965                         if( this.選択モードである )
1966                         {
1967                                 this.選択モード.Paint( e );
1968                         }
1969                         else
1970                         {
1971                                 this.編集モード.Paint( e );
1972                         }
1973                 }
1974                 protected void pictureBox譜面パネル_PreviewKeyDown( object sender, PreviewKeyDownEventArgs e )
1975                 {
1976                         if( Keys.Prior == e.KeyCode )
1977                         {
1978                                 #region " PageUp → 移動量に対応する grid だけ垂直つまみを移動させる。あとはこの移動で生じる ChangedValue イベントで処理。"
1979                                 //-----------------
1980                                 int 移動すべき数grid = -this.GRID_PER_PART;
1981                                 int 新しい位置 = this.vScrollBar譜面用垂直スクロールバー.Value + 移動すべき数grid;
1982                                 int 最小値 = this.vScrollBar譜面用垂直スクロールバー.Minimum;
1983                                 int 最大値 = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
1984
1985                                 if( 新しい位置 < 最小値 )
1986                                 {
1987                                         新しい位置 = 最小値;
1988                                 }
1989                                 else if( 新しい位置 > 最大値 )
1990                                 {
1991                                         新しい位置 = 最大値;
1992                                 }
1993                                 this.vScrollBar譜面用垂直スクロールバー.Value = 新しい位置;
1994                                 //-----------------
1995                                 #endregion
1996                         }
1997                         else if( Keys.Next == e.KeyCode )
1998                         {
1999                                 #region " PageDown → 移動量に対応する grid だけ垂直つまみを移動させる。あとはこの移動で生じる ChangedValue イベントで処理。"
2000                                 //-----------------
2001                                 int 移動すべき数grid = this.GRID_PER_PART;
2002                                 int 新しい位置 = this.vScrollBar譜面用垂直スクロールバー.Value + 移動すべき数grid;
2003                                 int 最小値 = this.vScrollBar譜面用垂直スクロールバー.Minimum;
2004                                 int 最大値 = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
2005
2006                                 if( 新しい位置 < 最小値 )
2007                                 {
2008                                         新しい位置 = 最小値;
2009                                 }
2010                                 else if( 新しい位置 > 最大値 )
2011                                 {
2012                                         新しい位置 = 最大値;
2013                                 }
2014                                 this.vScrollBar譜面用垂直スクロールバー.Value = 新しい位置;
2015                                 //-----------------
2016                                 #endregion
2017                         }
2018                         else
2019                         {
2020                                 // 編集モードオブジェクトへ処理を引き継ぐ。
2021                                 if( this.編集モードである )
2022                                         this.編集モード.PreviewKeyDown( e );
2023                         }
2024                 }
2025                 
2026                 protected void splitContainer分割パネルコンテナ_MouseWheel( object sender, MouseEventArgs e )
2027                 {
2028                         if( false == this.初期化完了 )
2029                                 return;     // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2030
2031                         #region " 移動量に対応する grid だけ垂直つまみを移動させる。あとはこの移動で生じる ChangedValue イベントで処理する。"
2032                         //-----------------
2033                         if( 0 == e.Delta )
2034                                 return;     // 移動量なし
2035
2036                         // e.Delta は、スクロールバーを下へ動かしたいときに負、上へ動かしたいときに正となる。
2037                         int 移動すべき行数 = ( -e.Delta * SystemInformation.MouseWheelScrollLines ) / 120;
2038
2039                         // 1行=1拍とする。
2040                         int 移動すべき数grid = 移動すべき行数 * ( this.GRID_PER_PART / 4 );
2041
2042                         // スクロールバーのつまみを移動する。
2043                         int 新しい位置 = this.vScrollBar譜面用垂直スクロールバー.Value + 移動すべき数grid;
2044                         int 最小値 = this.vScrollBar譜面用垂直スクロールバー.Minimum;
2045                         int 最大値 = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
2046
2047                         if( 新しい位置 < 最小値 )
2048                         {
2049                                 新しい位置 = 最小値;
2050                         }
2051                         else if( 新しい位置 > 最大値 )
2052                         {
2053                                 新しい位置 = 最大値;
2054                         }
2055                         this.vScrollBar譜面用垂直スクロールバー.Value = 新しい位置;
2056                         //-----------------
2057                         #endregion
2058                 }
2059                 protected void splitContainer分割パネルコンテナ_Panel2_SizeChanged( object sender, EventArgs e )
2060                 {
2061                         if( false == this.初期化完了 )
2062                                 return;         // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2063
2064                         this.垂直スクロールバーと譜面の上下位置を調整する();
2065                 }
2066                 protected void splitContainer分割パネルコンテナ_Panel2_Paint( object sender, PaintEventArgs e )
2067                 {
2068                         if( false == this.初期化完了 )
2069                                 return;         // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2070
2071                         var g = e.Graphics;
2072                         var メモ領域左上隅の位置 = new PointF() {
2073                                 X = this.譜面.レーンの合計幅px,
2074                                 Y = this.pictureBox譜面パネル.Location.Y,
2075                         };
2076
2077                         #region " 見出し<小節メモ>を描画する。"
2078                         //-----------------
2079                         g.DrawString( Properties.Resources.MSG_小節メモ, this.メモ用フォント, Brushes.White, PointF.Add( メモ領域左上隅の位置, new Size( 24, -24 )/*マージン*/ ) );
2080                         //-----------------
2081                         #endregion
2082                         #region " 小節メモを描画する。"
2083                         //-----------------
2084
2085                         // グリッド値は 上辺>下辺 なので注意。
2086                         int パネル下辺grid = this.譜面.現在の譜面表示下辺の譜面内絶対位置grid;
2087                         int パネル上辺grid = パネル下辺grid + ( this.pictureBox譜面パネル.ClientSize.Height * this.GRID_PER_PIXEL );
2088                         int 開始小節番号 = this.譜面.現在譜面表示下辺に存在している小節番号;
2089
2090                         for( int 小節番号 = 開始小節番号; 小節番号 <= this.譜面.SSTFormatScore.最大小節番号; 小節番号++ )
2091                         {
2092                                 int 小節の下辺grid = this.譜面.小節先頭の譜面内絶対位置gridを返す( 小節番号 );
2093                                 int 小節の上辺grid = 小節の下辺grid + this.譜面.小節長をグリッドで返す( 小節番号 );
2094
2095                                 if( 小節の下辺grid > パネル上辺grid )
2096                                         break;  // 小節が画面上方にはみ出し切ってしまったらそこで終了。
2097
2098                                 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2099                                 {
2100                                         string メモ = this.譜面.SSTFormatScore.dicメモ[ 小節番号 ];
2101
2102                                         string[] lines = メモ.Split( new string[] { Environment.NewLine }, StringSplitOptions.None );
2103                                         int 行数 = lines.Length;
2104
2105                                         var メモの位置 = new PointF() {
2106                                                 X = メモ領域左上隅の位置.X + 4,       // + 4 はマージン
2107                                                 Y = メモ領域左上隅の位置.Y + ( パネル上辺grid - 小節の下辺grid ) / this.GRID_PER_PIXEL - ( 行数 * 16 ),           // 9pt = だいたい16px 
2108                                         };
2109                                         g.DrawString( メモ, this.メモ用フォント, Brushes.White, メモの位置 );
2110                                 }
2111                         }
2112                         //-----------------
2113                         #endregion
2114                 }
2115
2116                 protected void vScrollBar譜面用垂直スクロールバー_ValueChanged( object sender, EventArgs e )
2117                 {
2118                         if( false == this.初期化完了 )
2119                                 return;         // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2120
2121                         var bar = vScrollBar譜面用垂直スクロールバー;
2122
2123                         if( bar.Enabled )
2124                         {
2125                                 // 下辺の位置を再計算。
2126                                 this.譜面.現在の譜面表示下辺の譜面内絶対位置grid = ( ( bar.Maximum + 1 ) - bar.LargeChange ) - bar.Value;
2127
2128                                 // 編集モードの場合、カーソルのgrid位置を再計算。
2129                                 if( this.編集モードである )
2130                                 {
2131                                         this.編集モード.MouseMove(
2132                                                 new MouseEventArgs( MouseButtons.None, 0, this.編集モード.現在のチップカーソル領域.X, this.編集モード.現在のチップカーソル領域.Y, 0 ) );
2133                                 }
2134
2135                                 // メモ用小節番号を再計算。
2136                                 this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2137                                 this.numericUpDownメモ用小節番号.Value = this.譜面.現在カレントラインに存在している小節番号;
2138                                 this.次のプロパティ変更がUndoRedoリストに載るようにする();
2139
2140                                 // 小節メモを再描画する。
2141                                 this.splitContainer分割パネルコンテナ.Panel2.Refresh();
2142                         }
2143                 }
2144                 //-----------------
2145                 #endregion
2146                 #region " 譜面右メニュー "
2147                 //-----------------
2148                 protected void toolStripMenuItem選択チップの切り取り_Click( object sender, EventArgs e )
2149                 {
2150                         this.Act切り取る();
2151                 }
2152                 protected void toolStripMenuItem選択チップのコピー_Click( object sender, EventArgs e )
2153                 {
2154                         this.Actコピーする();
2155                 }
2156                 protected void toolStripMenuItem選択チップの貼り付け_Click( object sender, EventArgs e )
2157                 {
2158                         // メニューが開かれたときのマウスの座標を取得。
2159                         // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2160                         var マウスの位置 = this.選択モードのコンテクストメニューを開いたときのマウスの位置;
2161
2162                         if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2163                                 return;         // クリックされた場所にレーンがないなら無視。
2164
2165                         // アクションを実行。
2166                         this.Act貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
2167                 }
2168                 protected void toolStripMenuItem選択チップの削除_Click( object sender, EventArgs e )
2169                 {
2170                         this.Act削除する();
2171                 }
2172                 protected void toolStripMenuItemすべてのチップの選択_Click( object sender, EventArgs e )
2173                 {
2174                         // 編集モードなら強制的に選択モードにする。
2175                         if( this.編集モードである )
2176                                 this.選択モードに切替えて関連GUIを設定する();
2177
2178                         // 全チップを選択。
2179                         this.選択モード.全チップを選択する();
2180                 }
2181                 protected void toolStripMenuItem小節長変更_Click( object sender, EventArgs e )
2182                 {
2183                         // メニューが開かれたときのマウスの座標を取得。
2184                         // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2185                         var マウスの位置 = this.選択モードのコンテクストメニューを開いたときのマウスの位置;
2186
2187                         if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2188                                 return;         // クリックされた場所にレーンがないなら無視。
2189
2190                         // アクションを実行。
2191                         this.Act小節長倍率を変更する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
2192                 }
2193                 protected void toolStripMenuItem小節の挿入_Click( object sender, EventArgs e )
2194                 {
2195                         // メニューが開かれたときのマウスの座標を取得。
2196                         // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2197                         var マウスの位置 = this.選択モードのコンテクストメニューを開いたときのマウスの位置;
2198
2199                         if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2200                                 return;         // クリックされた場所にレーンがないなら無視。
2201
2202                         // アクションを実行。
2203                         this.Act小節を挿入する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
2204                 }
2205                 protected void toolStripMenuItem小節の削除_Click( object sender, EventArgs e )
2206                 {
2207                         // メニューが開かれたときのマウスの座標を取得。
2208                         // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2209                         var マウスの位置 = this.選択モードのコンテクストメニューを開いたときのマウスの位置;
2210
2211                         if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2212                                 return;         // クリックされた場所にレーンがないなら無視。
2213
2214                         // アクションを実行。
2215                         this.Act小節を削除する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
2216                 }
2217                 //-----------------
2218                 #endregion
2219                 #region " 基本情報タブ "
2220                 //-----------------
2221                 protected void textBox曲名_TextChanged( object sender, EventArgs e )
2222                 {
2223                         #region " この変更が Undo/Redo したことによるものではない場合、UndoRedoセルを追加 or 修正する。"
2224                         //-----------------
2225                         if( false == UndoRedo.管理.UndoRedoした直後である )
2226                         {
2227                                 // 最新のセルの所有者が自分?
2228                                 var cell = this.UndoRedo管理.Undoするセルを取得して返す_見るだけ();
2229
2230                                 if( ( null != cell ) && cell.所有権がある( this.textBox曲名 ) )
2231                                 {
2232                                         // (A) 所有者である → 最新のセルの "変更後の値" を現在のコントロールの値に更新する。
2233                                         ( (UndoRedo.セル<string>) cell ).変更後の値 = this.textBox曲名.Text;
2234                                 }
2235                                 else
2236                                 {
2237                                         // (B) 所有者ではない → 以下のようにセルを新規追加する。
2238                                         //    "変更前の値" ← 以前の値
2239                                         //    "変更後の値" ← 現在の値
2240                                         //    "所有者ID" ← 対象となるコンポーネントオブジェクト
2241                                         var cc = new UndoRedo.セル<string>(
2242                                                 所有者ID: this.textBox曲名,
2243                                                 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {      
2244                                                         this.タブを選択する( タブ種別.基本情報 );
2245                                                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2246                                                         this.textBox曲名.Text = 変更前;
2247                                                         this.textBox曲名.Focus();
2248                                                 },
2249                                                 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2250                                                         this.タブを選択する( タブ種別.基本情報 );
2251                                                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2252                                                         this.textBox曲名.Text = 変更後;
2253                                                         this.textBox曲名.Focus();
2254                                                 },
2255                                                 変更対象: null,
2256                                                 変更前の値: this.textBox曲名_以前の値,
2257                                                 変更後の値: this.textBox曲名.Text,
2258                                                 任意1: null, 
2259                                                 任意2: null );
2260                                         
2261                                         this.UndoRedo管理.セルを追加する( cc );
2262
2263                                         // Undo ボタンを有効にする。
2264                                         this.UndoRedo用GUIのEnabledを設定する();
2265                                 }
2266                         }
2267                         //-----------------
2268                         #endregion
2269
2270                         this.textBox曲名_以前の値 = this.textBox曲名.Text;              // 以前の値 ← 現在の値
2271                         UndoRedo.管理.UndoRedoした直後である = false;
2272                         this.未保存である = true;
2273
2274                         // スコアには随時保存する。
2275                         譜面.SSTFormatScore.Header.曲名 = this.textBox曲名.Text;
2276                 }
2277                 protected void textBox曲名_Leave( object sender, EventArgs e )
2278                 {
2279                         // 最新の UndoRedoセル の所有権を放棄する。
2280                         this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBox曲名 );
2281                 }
2282                 protected string textBox曲名_以前の値 = "";
2283
2284                 protected void textBox説明_TextChanged( object sender, EventArgs e )
2285                 {
2286                         #region " この変更が Undo/Redo したことによるものではない場合、UndoRedoセルを追加 or 修正する。"
2287                         //-----------------
2288                         if( false == UndoRedo.管理.UndoRedoした直後である )
2289                         {
2290                                 // 最新のセルの所有者が自分?
2291                                 var cell = this.UndoRedo管理.Undoするセルを取得して返す_見るだけ();
2292                                 if( ( null != cell ) && cell.所有権がある( this.textBox説明 ) )
2293                                 {
2294                                         // (A) 所有者である → 最新のセルの "変更後の値" を現在のコントロールの値に更新。
2295
2296                                         ( (UndoRedo.セル<string>) cell ).変更後の値 = this.textBox説明.Text;
2297                                 }
2298                                 else
2299                                 {
2300                                         // (B) 所有者ではない → 以下のようにセルを新規追加する。
2301                                         //    "変更前の値" ← 以前の値
2302                                         //    "変更後の値" ← 現在の値
2303                                         //    "所有者ID" ← 対象となるコンポーネントオブジェクト
2304                                         var cc = new UndoRedo.セル<string>(
2305                                                 所有者ID: this.textBox説明,
2306                                                 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2307                                                         this.タブを選択する( タブ種別.基本情報 );
2308                                                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2309                                                         this.textBox説明.Text = 変更前;
2310                                                         this.textBox説明.Focus();
2311                                                 },
2312                                                 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2313                                                         this.タブを選択する( タブ種別.基本情報 );
2314                                                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2315                                                         this.textBox説明.Text = 変更後;
2316                                                         this.textBox説明.Focus();
2317                                                 },
2318                                                 変更対象: null,
2319                                                 変更前の値: this.textBox説明_以前の値, 
2320                                                 変更後の値: this.textBox説明.Text,
2321                                                 任意1: null,
2322                                                 任意2: null );
2323
2324                                         this.UndoRedo管理.セルを追加する( cc );
2325
2326                                         // Undo ボタンを有効にする。
2327                                         this.UndoRedo用GUIのEnabledを設定する();
2328                                 }
2329                         }
2330                         //-----------------
2331                         #endregion
2332
2333                         this.textBox説明_以前の値 = this.textBox説明.Text;      // 以前の値 ← 現在の値
2334                         UndoRedo.管理.UndoRedoした直後である = false;
2335                         this.未保存である = true;
2336
2337                         // スコアには随時保存する。
2338                         譜面.SSTFormatScore.Header.説明文 = this.textBox説明.Text;
2339                 }
2340                 protected void textBox説明_Leave( object sender, EventArgs e )
2341                 {
2342                         // 最新 UndoRedoセル の所有権を放棄する。
2343                         this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBox説明 );
2344                 }
2345                 protected string textBox説明_以前の値 = "";
2346
2347                 protected void textBoxメモ_TextChanged( object sender, EventArgs e )
2348                 {
2349                         #region " この変更が Undo/Redo したことによるものではない場合、UndoRedoセルを追加or修正する。"
2350                         //-----------------
2351                         if( !UndoRedo.管理.UndoRedoした直後である )
2352                         {
2353                                 // 最新のセルの所有者が自分?
2354
2355                                 UndoRedo.セルBase cell = this.UndoRedo管理.Undoするセルを取得して返す_見るだけ();
2356
2357                                 if( ( cell != null ) && cell.所有権がある( this.textBoxメモ ) )
2358                                 {
2359                                         // (Yes) 最新のセルの "変更後の値" を <現在の値> に更新。
2360
2361                                         ( (UndoRedo.セル<string>) cell ).変更後の値 = this.textBoxメモ.Text;
2362                                 }
2363                                 else
2364                                 {
2365                                         // (No) セルを新規追加:
2366                                         //      "変更前の値" = <以前の値>
2367                                         //      "変更後の値" = <現在の値>
2368                                         //      "所有者ID" = 対象となるコンポーネントオブジェクト
2369
2370                                         var cc = new UndoRedo.セル<string>(
2371                                                 所有者ID: this.textBoxメモ,
2372                                                 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2373                                                         this.タブを選択する( タブ種別.基本情報 );
2374                                                         this.numericUpDownメモ用小節番号.Value = (decimal) 任意1;
2375                                                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2376                                                         this.textBoxメモ.Text = 変更前;
2377                                                         this.textBoxメモ.Focus();
2378
2379                                                         int 小節番号 = (int) ( (decimal) 任意1 );
2380
2381                                                         #region " dicメモ の更新 "
2382                                                         //-----------------
2383                                                         if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2384                                                         {
2385                                                                 if( string.IsNullOrEmpty( 変更前 ) )
2386                                                                         this.譜面.SSTFormatScore.dicメモ.Remove( 小節番号 );
2387                                                                 else
2388                                                                         this.譜面.SSTFormatScore.dicメモ[ 小節番号 ] = 変更前;
2389                                                         }
2390                                                         else
2391                                                         {
2392                                                                 if( !string.IsNullOrEmpty( 変更前 ) )
2393                                                                         this.譜面.SSTFormatScore.dicメモ.Add( 小節番号, 変更前 );
2394                                                         }
2395                                                         //-----------------
2396                                                         #endregion
2397
2398                                                         this.Act小節の先頭へ移動する( 小節番号 );
2399                                                         this.splitContainer分割パネルコンテナ.Panel2.Refresh();        // 小節メモをリフレッシュ。
2400                                                 },
2401                                                 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2402                                                         this.タブを選択する( タブ種別.基本情報 );
2403                                                         this.numericUpDownメモ用小節番号.Value = (decimal) 任意1;
2404                                                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2405                                                         this.textBoxメモ.Text = 変更後;
2406                                                         this.textBoxメモ.Focus();
2407
2408                                                         int 小節番号 = (int) ( (decimal) 任意1 );
2409
2410                                                         #region " dicメモの更新 "
2411                                                         //-----------------
2412                                                         if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2413                                                         {
2414                                                                 if( string.IsNullOrEmpty( 変更後 ) )
2415                                                                         this.譜面.SSTFormatScore.dicメモ.Remove( 小節番号 );
2416                                                                 else
2417                                                                         this.譜面.SSTFormatScore.dicメモ[ 小節番号 ] = 変更後;
2418                                                         }
2419                                                         else
2420                                                         {
2421                                                                 if( !string.IsNullOrEmpty( 変更後 ) )
2422                                                                         this.譜面.SSTFormatScore.dicメモ.Add( 小節番号, 変更後 );
2423                                                         }
2424                                                         //-----------------
2425                                                         #endregion
2426
2427                                                         this.Act小節の先頭へ移動する( 小節番号 );
2428                                                         this.splitContainer分割パネルコンテナ.Panel2.Refresh();        // 小節メモをリフレッシュ。
2429                                                 },
2430                                                 変更対象: null,
2431                                                 変更前の値: this.textBoxメモ_以前の値,
2432                                                 変更後の値: this.textBoxメモ.Text,
2433                                                 任意1: (object) this.numericUpDownメモ用小節番号.Value, 
2434                                                 任意2: null );
2435
2436                                         this.UndoRedo管理.セルを追加する( cc );
2437
2438                                         // Undo ボタンを有効にする。
2439                                         this.UndoRedo用GUIのEnabledを設定する();
2440                                 }
2441                         }
2442                         //-----------------
2443                         #endregion
2444
2445                         this.textBoxメモ_以前の値 = this.textBoxメモ.Text;      // <以前の値> = <現在の値>
2446
2447                         if( false == UndoRedo.管理.UndoRedoした直後である )
2448                                 this.未保存である = true;
2449
2450                         UndoRedo.管理.UndoRedoした直後である = false;
2451
2452                         #region " 小節番号に対応するメモを dicメモ に登録する。"
2453                         //-----------------
2454                         {
2455                                 int 小節番号 = (int) this.numericUpDownメモ用小節番号.Value;
2456
2457                                 if( string.IsNullOrEmpty( this.textBoxメモ.Text ) )
2458                                 {
2459                                         // (A) 空文字列の場合
2460                                         if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2461                                                 this.譜面.SSTFormatScore.dicメモ.Remove( 小節番号 );            // 存在してたら削除。
2462                                         // 存在してなかったら何もしない。
2463                                 }
2464                                 else
2465                                 {
2466                                         // (B) その他の場合
2467                                         if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2468                                                 this.譜面.SSTFormatScore.dicメモ[ 小節番号 ] = this.textBoxメモ.Text;         // 存在してたら更新。
2469                                         else
2470                                                 this.譜面.SSTFormatScore.dicメモ.Add( 小節番号, this.textBoxメモ.Text );      // 存在してなかったら追加。
2471                                 }
2472                         }
2473                         //-----------------
2474                         #endregion
2475                         #region " もし最終小節だったなら、後ろに4つ小節を加える。"
2476                         //-----------------
2477                         {
2478                                 int 小節番号 = (int) this.numericUpDownメモ用小節番号.Value;
2479                                 if( 小節番号 == this.譜面.SSTFormatScore.最大小節番号 )
2480                                 {
2481                                         this.譜面.最後の小節の後ろに小節を4つ追加する();
2482                                 }
2483                         }
2484                         //-----------------
2485                         #endregion
2486                 }
2487                 protected void textBoxメモ_Leave( object sender, EventArgs e )
2488                 {
2489                         // 最新 UndoRedoセル の所有権を放棄する。
2490                         this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBoxメモ );
2491
2492                         // 小節メモをリフレッシュ。
2493                         this.splitContainer分割パネルコンテナ.Panel2.Refresh();
2494                 }
2495                 protected string textBoxメモ_以前の値 = "";
2496
2497                 protected void numericUpDownメモ用小節番号_ValueChanged( object sender, EventArgs e )
2498                 {
2499                         // 小節番号にあわせて、textBoxメモにメモを表示する。
2500                         int 小節番号 = (int) this.numericUpDownメモ用小節番号.Value;
2501                         this.次のプロパティ変更がUndoRedoリストに載らないようにする();
2502                         if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2503                                 this.textBoxメモ.Text = this.譜面.SSTFormatScore.dicメモ[ 小節番号 ];
2504                         else
2505                                 this.textBoxメモ.Text = "";
2506                         this.次のプロパティ変更がUndoRedoリストに載るようにする();
2507                 }
2508                 //-----------------
2509                 #endregion
2510
2511                 private Font メモ用フォント = new Font( FontFamily.GenericSansSerif, 9.0f );
2512                 private readonly Dictionary<int, string> dic音量ラベル = new Dictionary<int, string>() {
2513                         { 1, "1 (Smallest)" },
2514                         { 2, "2 (Smaller)" },
2515                         { 3, "3 (Middle)" },
2516                         { 4, "4 (Normal)" },
2517                 };
2518         }
2519 }