OSDN Git Service

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