OSDN Git Service

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