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

OSDN Git Service

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