OSDN Git Service

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