2 using System.Collections.Generic;
3 using System.Diagnostics;
8 using System.Reflection;
9 using System.ServiceModel;
10 using System.ServiceModel.Channels;
11 using System.Text.RegularExpressions;
12 using System.Windows.Forms;
13 using FDK; // for string拡張
17 partial class メインフォーム : Form
21 public int メジャーバージョン番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Major );
22 public int マイナーバージョン番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Minor );
23 public int リビジョン番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Revision );
24 public int ビルド番号 => ( Assembly.GetExecutingAssembly().GetName().Version.Build );
30 public const int 最大音量 = 4;
31 public const int 最小音量 = 1;
46 // ウィンドウのタイトルバーの文字列を修正する。
47 string 表示するファイルの名前 = ( string.IsNullOrEmpty( this._編集中のファイル名 ) ) ? Properties.Resources.NEW_FILENAME : this._編集中のファイル名;
50 // 変更ありかつ未保存なら「*」を付ける。
51 this.Text = $"SSTFEditor {this.メジャーバージョン番号}.{this.マイナーバージョン番号}.{this.リビジョン番号}.{this.ビルド番号} *[{表示するファイルの名前}]";
52 this.toolStripMenuItem上書き保存.Enabled = true;
53 this.toolStripButton上書き保存.Enabled = true;
57 // 保存後変更がないなら「*」は付けない。
58 this.Text = $"SSTFEditor {this.メジャーバージョン番号}.{this.マイナーバージョン番号}.{this.リビジョン番号}.{this.ビルド番号} [{表示するファイルの名前}]";
59 this.toolStripMenuItem上書き保存.Enabled = false;
60 this.toolStripButton上書き保存.Enabled = false;
65 public bool 選択モードである => ( ( CheckState.Checked == this.toolStripButton選択モード.CheckState ) ? true : false );
66 public bool 編集モードである => ( ( CheckState.Checked == this.toolStripButton編集モード.CheckState ) ? true : false );
70 /// 小節長倍率が 1.0 でない場合は、これを乗じることでグリッド数が変化する。
72 public int GRID_PER_PART => int.Parse( Properties.Resources.GRID_PER_PART );
78 public int GRID_PER_PIXEL => (int) ( int.Parse( Properties.Resources.GRID_PER_PIXEL ) / ( 1 + 0.25 * this.toolStripComboBox譜面拡大率.SelectedIndex ) );
84 public UndoRedo.管理 UndoRedo管理;
85 public クリップボード クリップボード;
86 public Size 譜面パネルサイズ => ( this.pictureBox譜面パネル.ClientSize );
87 public SSTFormat.チップ種別 現在のチップ種別
91 return this._現在のチップ種別;
95 this._現在のチップ種別 = value;
96 this.label現在のチップ種別.Text = this._チップto名前[ value ];
99 public int 現在のチップ音量 = メインフォーム.最大音量;
100 public bool 初期化完了 = false;
104 InitializeComponent();
108 public void 選択モードに切替えて関連GUIを設定する()
110 this.toolStripButton選択モード.CheckState = CheckState.Checked;
111 this.toolStripButton編集モード.CheckState = CheckState.Unchecked;
113 this.toolStripMenuItem選択モード.CheckState = CheckState.Checked;
114 this.toolStripMenuItem編集モード.CheckState = CheckState.Unchecked;
116 this.label現在のチップ種別.Text = "----";
120 public void 編集モードに切替えて関連GUIを設定する()
122 this.選択モード.全チップの選択を解除する();
125 this.toolStripButton選択モード.CheckState = CheckState.Unchecked;
126 this.toolStripButton編集モード.CheckState = CheckState.Checked;
128 this.toolStripMenuItem選択モード.CheckState = CheckState.Unchecked;
129 this.toolStripMenuItem編集モード.CheckState = CheckState.Checked;
131 public void 選択チップの有無に応じて編集用GUIのEnabledを設定する()
133 bool 譜面上に選択チップがある = this._選択チップが1個以上ある;
134 bool クリップボードに選択チップがある = ( null != this.クリップボード ) && ( 0 < this.クリップボード.セル数 );
136 // 編集メニューの Enabled 設定
137 this.toolStripMenuItemコピー.Enabled = 譜面上に選択チップがある;
138 this.toolStripMenuItem切り取り.Enabled = 譜面上に選択チップがある;
139 this.toolStripMenuItem貼り付け.Enabled = クリップボードに選択チップがある;
140 this.toolStripMenuItem削除.Enabled = 譜面上に選択チップがある;
143 this.toolStripButtonコピー.Enabled = 譜面上に選択チップがある;
144 this.toolStripButton切り取り.Enabled = 譜面上に選択チップがある;
145 this.toolStripButton貼り付け.Enabled = クリップボードに選択チップがある;
146 this.toolStripButton削除.Enabled = 譜面上に選択チップがある;
149 this.toolStripMenuItem選択チップのコピー.Enabled = 譜面上に選択チップがある;
150 this.toolStripMenuItem選択チップの切り取り.Enabled = 譜面上に選択チップがある;
151 this.toolStripMenuItem選択チップの貼り付け.Enabled = クリップボードに選択チップがある;
152 this.toolStripMenuItem選択チップの削除.Enabled = 譜面上に選択チップがある;
154 public void 譜面をリフレッシュする()
156 this.pictureBox譜面パネル.Refresh();
158 public void UndoRedo用GUIのEnabledを設定する()
160 bool Undo可 = ( 0 < this.UndoRedo管理.Undo可能な回数 ) ? true : false;
161 bool Redo可 = ( 0 < this.UndoRedo管理.Redo可能な回数 ) ? true : false;
163 this.toolStripMenuItem元に戻す.Enabled = Undo可;
164 this.toolStripMenuItemやり直す.Enabled = Redo可;
165 this.toolStripButton元に戻す.Enabled = Undo可;
166 this.toolStripButtonやり直す.Enabled = Redo可;
168 public void 選択モードのコンテクストメニューを表示する( int x, int y )
170 this.contextMenuStrip譜面右メニュー.Show( this.pictureBox譜面パネル, x, y );
172 // メニューを表示した時のマウス座標を控えておく。
173 this._選択モードでコンテクストメニューを開いたときのマウスの位置 = new Point( x, y );
175 public void 譜面を縦スクロールする( int スクロール量grid )
177 int 現在の位置grid = this.vScrollBar譜面用垂直スクロールバー.Value;
178 int スクロール後の位置grid = this.vScrollBar譜面用垂直スクロールバー.Value + スクロール量grid;
179 int 最小値grid = this.vScrollBar譜面用垂直スクロールバー.Minimum;
180 int 最大値grid = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
182 if( スクロール後の位置grid < 最小値grid )
184 スクロール後の位置grid = 最小値grid;
186 else if( スクロール後の位置grid > 最大値grid )
188 スクロール後の位置grid = 最大値grid;
191 this.vScrollBar譜面用垂直スクロールバー.Value = スクロール後の位置grid;
194 private bool _未保存である = false;
196 private readonly Dictionary<SSTFormat.チップ種別, string> _チップto名前
199 = new Dictionary<SSTFormat.チップ種別, string>() {
200 { SSTFormat.チップ種別.Bass, "BassDrum" },
201 { SSTFormat.チップ種別.BPM, "BPM" },
202 { SSTFormat.チップ種別.China, "China" },
203 { SSTFormat.チップ種別.HiHat_Close, "HiHat(Close)" },
204 { SSTFormat.チップ種別.HiHat_Foot, "FootPedal" },
205 { SSTFormat.チップ種別.HiHat_HalfOpen, "HiHat(HarfOpen)" },
206 { SSTFormat.チップ種別.HiHat_Open, "HiHat(Open)" },
207 { SSTFormat.チップ種別.LeftCrash, "Crash" },
208 { SSTFormat.チップ種別.Ride, "Ride" },
209 { SSTFormat.チップ種別.Ride_Cup, "Ride(Cup)" },
210 { SSTFormat.チップ種別.RightCrash, "Crash" },
211 { SSTFormat.チップ種別.Snare, "Snare" },
212 { SSTFormat.チップ種別.Snare_ClosedRim, "Snare(CloseRimShot)" },
213 { SSTFormat.チップ種別.Snare_Ghost, "Snare(Ghost)" },
214 { SSTFormat.チップ種別.Snare_OpenRim, "Snare(OpenRimShot)" },
215 { SSTFormat.チップ種別.Splash, "Splash" },
216 { SSTFormat.チップ種別.Tom1, "HighTom" },
217 { SSTFormat.チップ種別.Tom1_Rim, "HighTom(RimShot)" },
218 { SSTFormat.チップ種別.Tom2, "LowTom" },
219 { SSTFormat.チップ種別.Tom2_Rim, "LowTom(RimShot)" },
220 { SSTFormat.チップ種別.Tom3, "FloorTom" },
221 { SSTFormat.チップ種別.Tom3_Rim, "FloorTom(RimShot)" },
222 { SSTFormat.チップ種別.LeftCymbal_Mute, "Mute" },
223 { SSTFormat.チップ種別.RightCymbal_Mute, "Mute" },
224 { SSTFormat.チップ種別.Unknown, "" },
225 { SSTFormat.チップ種別.小節線, "" },
226 { SSTFormat.チップ種別.背景動画, "" },
227 { SSTFormat.チップ種別.拍線, "" },
228 { SSTFormat.チップ種別.小節メモ, "" },
233 private Font _メモ用フォント = new Font( FontFamily.GenericSansSerif, 9.0f );
235 private readonly Dictionary<int, string> _音量toラベル
238 = new Dictionary<int, string>() {
239 { 1, "1 (Smallest)" },
240 { 2, "2 (Smaller)" },
247 private bool _選択チップが1個以上ある
251 if( ( null != this.譜面.SSTFormatScore ) &&
252 ( null != this.譜面.SSTFormatScore.チップリスト ) &&
253 ( 0 < this.譜面.SSTFormatScore.チップリスト.Count ) )
255 foreach( var chip in this.譜面.SSTFormatScore.チップリスト )
266 /// 単位: [n分の1] (例: 間隔=16 なら、"1/16" を意味する。)
268 private int _現在のガイド間隔 = 0;
270 private Point _選択モードでコンテクストメニューを開いたときのマウスの位置;
272 private SSTFormat.チップ種別 _現在のチップ種別 = SSTFormat.チップ種別.Unknown;
275 #region " フォルダ、ファイルパス "
279 /// SSTFEditor.exe と StrokeStyleT.exe が格納されているフォルダへのパス。
281 private string _システムフォルダパス => ( Path.GetDirectoryName( Application.ExecutablePath ) );
284 /// Windowsログインユーザのアプリデータフォルダ。末尾は '\'。
287 /// 例: "C:\Users\ログインユーザ名\ApplicationData\SSTFEditor\"
289 private string _ユーザフォルダパス
293 var path = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create ), @"SSTFEditor\" );
295 if( false == Directory.Exists( path ) )
296 Directory.CreateDirectory( path ); // なければ作成する。
302 private string _作業フォルダパス = null;
304 private string _編集中のファイル名 = null;
306 private string _最後にプレイヤーに渡した一時ファイル名 = null;
313 private ChannelFactory<SST.IStrokeStyleTService> _SSTファクトリ = null;
315 private SST.IStrokeStyleTService _SSTサービス = null;
317 private IClientChannel _SSTサービスチャンネル = null;
324 #region " アクションメソッド(最上位の機能エントリ。メニューやコマンドバーなどから呼び出される。)"
326 private void _アプリの起動処理を行う()
328 //this.GRID_PER_PART = int.Parse( Properties.Resources.GRID_PER_PART );
329 //this._GRID_PER_PIXEL = int.Parse( Properties.Resources.GRID_PER_PIXEL );
331 // 作業フォルダの初期値は、Windowsユーザのマイドキュメントフォルダ。
332 this._作業フォルダパス = Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments );
335 this.Config = Config.読み込む( Path.Combine( this._ユーザフォルダパス, Properties.Resources.CONFIG_FILE_NAME ) );
336 if( this.Config.ViewerPath.Nullまたは空である() )
338 // 既定のプレイヤーは、exe と同じ場所にあるものとする。
339 this.Config.ViewerPath = Path.Combine( this._システムフォルダパス, Properties.Resources.PLAYER_NAME );
341 if( false == File.Exists( this.Config.ViewerPath ) )
342 this.Config.ViewerPath = ""; // ビュアーが存在してない。
345 // デザイナでは追加できないイベントを手動で追加する。
346 this.splitContainer分割パネルコンテナ.MouseWheel += new MouseEventHandler( splitContainer分割パネルコンテナ_MouseWheel );
349 this._ConfigのRecentUsedFilesをファイルメニューへ追加する();
353 this._ガイド間隔を変更する( 16 ); // 初期は 1/16 間隔。
354 this._譜面拡大率を変更する( 1 ); // 初期は 標準。
359 // コマンドライン引数に、存在するファイルの指定があれば開く。
360 foreach( var arg in Environment.GetCommandLineArgs().Skip( 1 ) ) // 先頭は exe 名なのでスキップ。
362 if( Path.GetExtension( arg ).ToLower() == ".sstf" && File.Exists( arg ) )
364 this._指定されたファイルを開く( arg );
369 private void _アプリの終了処理を行う()
374 this._SSTサービスチャンネル?.Close();
375 this._SSTサービスチャンネル = null;
376 this._SSTサービス = null;
378 this._SSTファクトリ?.Close();
379 this._SSTファクトリ = null;
386 // 一時ファイルが残っていれば、削除する。
387 if( File.Exists( this._最後にプレイヤーに渡した一時ファイル名 ) )
388 File.Delete( this._最後にプレイヤーに渡した一時ファイル名 );
391 this.Config.保存する( Path.Combine( this._ユーザフォルダパス, Properties.Resources.CONFIG_FILE_NAME ) );
393 FDK.Utilities.解放する( ref this.譜面 );
394 FDK.Utilities.解放する( ref this.選択モード );
397 private void _新規作成する()
399 if( DialogResult.Cancel == this._未保存なら保存する() )
400 return; // 保存がキャンセルされた場合はここで中断。
406 if( DialogResult.Cancel == this._未保存なら保存する() )
407 return; // 保存がキャンセルされた場合はここで中断。
409 #region " [ファイルを開く] ダイアログでファイルを選択する。"
411 var dialog = new OpenFileDialog() {
412 Title = Properties.Resources.MSG_ファイル選択ダイアログのタイトル,
413 Filter = Properties.Resources.MSG_曲ファイル選択ダイアログのフィルタ,
415 InitialDirectory = this._作業フォルダパス,
417 var result = dialog.ShowDialog( this );
419 // メインフォームを再描画してダイアログを完全に消す。
423 if( DialogResult.OK != result )
428 this._ファイルを読み込む( dialog.FileName );
430 private void _指定されたファイルを開く( string ファイルパス )
432 if( DialogResult.Cancel == this._未保存なら保存する() )
433 return; // 保存がキャンセルされた場合はここで中断。
435 this._ファイルを読み込む( ファイルパス );
438 private void _上書き保存する()
440 #region " ファイル名が未設定なら、初めての保存と見なし、ファイル保存ダイアログで保存ファイル名を指定させる。"
442 if( this._編集中のファイル名.Nullまたは空である() )
444 string 絶対パスファイル名 = this._ファイル保存ダイアログを開いてファイル名を取得する();
446 if( string.IsNullOrEmpty( 絶対パスファイル名 ) )
447 return; // ファイル保存ダイアログがキャンセルされたのならここで打ち切り。
449 this._作業フォルダパス = Path.GetDirectoryName( 絶対パスファイル名 ); // ダイアログでディレクトリを変更した場合、カレントディレクトリも変更されている。
450 this._編集中のファイル名 = Path.GetFileName( 絶対パスファイル名 );
456 Path.Combine( this._作業フォルダパス, this._編集中のファイル名 ),
459 private void _上書き保存する( string ファイルの絶対パス, bool 一時ファイルである )
461 #region " [保存中です] ポップアップを表示する。"
463 var msg = new Popupメッセージ(
464 Properties.Resources.MSG_保存中です + Environment.NewLine +
465 Properties.Resources.MSG_しばらくお待ち下さい );
474 // 選択モードだったら、全チップの選択を解除する。
476 this.選択モード.全チップの選択を解除する();
478 // 出力するパス内の背景動画を検索し、背景動画テキストボックスに設定する。
479 this.textBox背景動画.Text =
480 ( from ファイル名 in Directory.GetFiles( Path.GetDirectoryName( ファイルの絶対パス ) )
481 where SSTFormat.スコア.背景動画のデフォルト拡張子s.Any( 拡張子名 => ( Path.GetExtension( ファイル名 ).ToLower() == 拡張子名 ) )
482 select ファイル名 ).FirstOrDefault();
485 this.譜面.SSTFファイルを書き出す(
487 $"# Created by SSTFEditor {this.メジャーバージョン番号}.{this.マイナーバージョン番号}.{this.リビジョン番号}.{this.ビルド番号}" );
489 // 出力したファイルのパスを、[ファイル]メニューの最近使ったファイル一覧に追加する。
490 if( false == 一時ファイルである )
492 this.Config.ファイルを最近使ったファイルの一覧に追加する( ファイルの絶対パス );
493 this._ConfigのRecentUsedFilesをファイルメニューへ追加する();
498 #region " [保存中です] ポップアップを閉じる。"
504 // 最後に、ダイアログのゴミなどを消すために再描画。
507 // 一時ファイルじゃないなら、保存完了。
508 if( false == 一時ファイルである )
512 private void _名前を付けて保存する()
514 #region " ユーザに保存ファイル名を入力させる。"
516 string 絶対パスファイル名 = this._ファイル保存ダイアログを開いてファイル名を取得する();
518 if( string.IsNullOrEmpty( 絶対パスファイル名 ) )
519 return; // キャンセルされたらここで中断。
521 this._作業フォルダパス = Path.GetDirectoryName( 絶対パスファイル名 );
522 this._編集中のファイル名 = Path.GetFileName( 絶対パスファイル名 );
528 this.未保存である = true; // ウィンドウタイトルに表示されているファイル名を変更するため、一度わざと true にする。
539 // Undo する対象を UndoRedoリストから取得する。
540 var cell = this.UndoRedo管理.Undoするセルを取得して返す();
548 this.UndoRedo用GUIのEnabledを設定する();
549 this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
554 // Redo する対象を UndoRedoリストから取得する。
555 var cell = this.UndoRedo管理.Redoするセルを取得して返す();
563 this.UndoRedo用GUIのEnabledを設定する();
564 this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
570 // 譜面にフォーカスがないなら、何もしない。
571 if( false == this.pictureBox譜面パネル.Focused )
578 private void _コピーする()
580 // 譜面にフォーカスがないなら何もしない。
581 if( false == this.pictureBox譜面パネル.Focused )
585 this.クリップボード.現在選択されているチップをボードにコピーする();
588 this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
591 private void _貼り付ける( int 貼り付けを開始する譜面内絶対位置grid )
593 // 譜面にフォーカスがないなら何もしない。
594 if( false == this.pictureBox譜面パネル.Focused )
597 this.クリップボード.チップを指定位置から貼り付ける( 貼り付けを開始する譜面内絶対位置grid );
601 // 譜面にフォーカスがないなら何もしない。
602 if( false == this.pictureBox譜面パネル.Focused )
607 this.UndoRedo管理.トランザクション記録を開始する();
609 // 譜面が持つすべてのチップについて、選択されているチップがあれば削除する。
610 for( int i = this.譜面.SSTFormatScore.チップリスト.Count - 1; 0 <= i; i-- )
612 var chip = this.譜面.SSTFormatScore.チップリスト[ i ];
616 var chip変更前 = new SSTFormat.チップ( chip );
618 var cell = new UndoRedo.セル<SSTFormat.チップ>(
620 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
621 変更対象.CopyFrom( 変更前 );
622 this.譜面.SSTFormatScore.チップリスト.Add( 変更対象 );
623 this.譜面.SSTFormatScore.チップリスト.Sort();
625 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
626 this.譜面.SSTFormatScore.チップリスト.Remove( 変更対象 );
635 this.UndoRedo管理.セルを追加する( cell );
642 this.UndoRedo管理.トランザクション記録を終了する();
645 this.UndoRedo用GUIのEnabledを設定する();
646 this.選択チップの有無に応じて編集用GUIのEnabledを設定する();
651 private void _すべて選択する()
653 // 編集モードなら強制的に選択モードにする。
655 this.選択モードに切替えて関連GUIを設定する();
657 this.選択モード.全チップを選択する();
660 private void _選択モードにする()
662 this.選択モードに切替えて関連GUIを設定する();
664 private void _編集モードにする()
666 this.編集モードに切替えて関連GUIを設定する();
668 private void _モードを切替える()
672 this.編集モードに切替えて関連GUIを設定する();
676 this.選択モードに切替えて関連GUIを設定する();
682 this.選択モード.検索する(); // 現在のモードに関わらず、検索はすべて選択モードオブジェクトが行う。
685 private void _ガイド間隔を変更する( int n分 )
688 if( !( new[] { 4, 6, 8, 12, 16, 24, 32, 48, 64, 128, 0 }.Contains( n分 ) ) )
689 throw new ArgumentException( $"不正なガイド間隔({n分})が指定されました。" );
693 this.譜面.現在のガイド間隔を変更する( n分 ); // 譜面オブジェクトとも共有する。
695 #region " ガイド間隔関連GUI(メニュー、コンボボックス)を更新する。"
698 // 一度、すべてのガイド間隔メニューのチェックをはずす。
699 this.toolStripMenuItemガイド間隔4分.CheckState = CheckState.Unchecked;
700 this.toolStripMenuItemガイド間隔6分.CheckState = CheckState.Unchecked;
701 this.toolStripMenuItemガイド間隔8分.CheckState = CheckState.Unchecked;
702 this.toolStripMenuItemガイド間隔12分.CheckState = CheckState.Unchecked;
703 this.toolStripMenuItemガイド間隔16分.CheckState = CheckState.Unchecked;
704 this.toolStripMenuItemガイド間隔24分.CheckState = CheckState.Unchecked;
705 this.toolStripMenuItemガイド間隔32分.CheckState = CheckState.Unchecked;
706 this.toolStripMenuItemガイド間隔48分.CheckState = CheckState.Unchecked;
707 this.toolStripMenuItemガイド間隔64分.CheckState = CheckState.Unchecked;
708 this.toolStripMenuItemガイド間隔128分.CheckState = CheckState.Unchecked;
709 this.toolStripMenuItemガイド間隔フリー.CheckState = CheckState.Unchecked;
711 // で、制定された間隔に対応するメニューだけをチェックする。
714 // Menu と ComboBox の2つを変更することでイベントが2つ発生し、最終的に
715 // _ガイド間隔を変更する() を立て続けに2回呼び出してしまうことになるが……、まぁよしとする。
717 this.toolStripMenuItemガイド間隔4分.CheckState = CheckState.Checked;
718 this.toolStripComboBoxガイド間隔.SelectedIndex = 0;
722 this.toolStripMenuItemガイド間隔6分.CheckState = CheckState.Checked;
723 this.toolStripComboBoxガイド間隔.SelectedIndex = 1;
727 this.toolStripMenuItemガイド間隔8分.CheckState = CheckState.Checked;
728 this.toolStripComboBoxガイド間隔.SelectedIndex = 2;
732 this.toolStripMenuItemガイド間隔12分.CheckState = CheckState.Checked;
733 this.toolStripComboBoxガイド間隔.SelectedIndex = 3;
737 this.toolStripMenuItemガイド間隔16分.CheckState = CheckState.Checked;
738 this.toolStripComboBoxガイド間隔.SelectedIndex = 4;
742 this.toolStripMenuItemガイド間隔24分.CheckState = CheckState.Checked;
743 this.toolStripComboBoxガイド間隔.SelectedIndex = 5;
747 this.toolStripMenuItemガイド間隔32分.CheckState = CheckState.Checked;
748 this.toolStripComboBoxガイド間隔.SelectedIndex = 6;
752 this.toolStripMenuItemガイド間隔48分.CheckState = CheckState.Checked;
753 this.toolStripComboBoxガイド間隔.SelectedIndex = 7;
757 this.toolStripMenuItemガイド間隔64分.CheckState = CheckState.Checked;
758 this.toolStripComboBoxガイド間隔.SelectedIndex = 8;
762 this.toolStripMenuItemガイド間隔128分.CheckState = CheckState.Checked;
763 this.toolStripComboBoxガイド間隔.SelectedIndex = 9;
767 this.toolStripMenuItemガイド間隔フリー.CheckState = CheckState.Checked;
768 this.toolStripComboBoxガイド間隔.SelectedIndex = 10;
775 this.pictureBox譜面パネル.Invalidate();
777 private void _ガイド間隔を拡大する()
779 switch( this._現在のガイド間隔 )
782 case 6: this._ガイド間隔を変更する( 4 ); break;
783 case 8: this._ガイド間隔を変更する( 6 ); break;
784 case 12: this._ガイド間隔を変更する( 8 ); break;
785 case 16: this._ガイド間隔を変更する( 12 ); break;
786 case 24: this._ガイド間隔を変更する( 16 ); break;
787 case 32: this._ガイド間隔を変更する( 24 ); break;
788 case 48: this._ガイド間隔を変更する( 32 ); break;
789 case 64: this._ガイド間隔を変更する( 48 ); break;
790 case 128: this._ガイド間隔を変更する( 64 ); break;
791 case 0: this._ガイド間隔を変更する( 128 ); break;
794 private void _ガイド間隔を縮小する()
796 switch( this._現在のガイド間隔 )
798 case 4: this._ガイド間隔を変更する( 6 ); break;
799 case 6: this._ガイド間隔を変更する( 8 ); break;
800 case 8: this._ガイド間隔を変更する( 12 ); break;
801 case 12: this._ガイド間隔を変更する( 16 ); break;
802 case 16: this._ガイド間隔を変更する( 24 ); break;
803 case 24: this._ガイド間隔を変更する( 32 ); break;
804 case 32: this._ガイド間隔を変更する( 48 ); break;
805 case 48: this._ガイド間隔を変更する( 64 ); break;
806 case 64: this._ガイド間隔を変更する( 128 ); break;
807 case 128: this._ガイド間隔を変更する( 0 ); break;
812 private void _譜面拡大率を変更する( int n倍 )
814 if( ( 1 > n倍 ) || ( 10 < n倍 ) )
815 throw new ArgumentException( $"不正な譜面拡大率({n倍})が指定されました。" );
817 this.toolStripComboBox譜面拡大率.SelectedIndex = ( n倍 - 1 );
822 private void _最初から再生する()
824 this._指定された小節の先頭から再生する( 小節番号: 0 );
826 private void _現在位置から再生する()
829 int 小節番号 = this.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面.カレントラインの譜面内絶対位置grid, out 位置grid );
830 this._指定された小節の先頭から再生する( 小節番号 );
832 private void _現在位置からBGMのみ再生する()
835 int 小節番号 = this.譜面.譜面内絶対位置gridにおける小節の番号と小節先頭の位置gridを返す( this.譜面.カレントラインの譜面内絶対位置grid, out 位置grid );
836 this._指定された小節の先頭から再生する( 小節番号, ドラム音を発声する: false );
838 private void _指定された小節の先頭から再生する( int 小節番号, bool ドラム音を発声する = true )
840 if( ( this.Config.ViewerPath.Nullまたは空である() ) ||
841 ( false == File.Exists( this.Config.ViewerPath ) ) )
844 // 前回の一時ファイルが存在していれば削除する。
845 if( File.Exists( this._最後にプレイヤーに渡した一時ファイル名 ) )
846 File.Delete( this._最後にプレイヤーに渡した一時ファイル名 );
848 // 新しい一時ファイルの名前をランダムに決める。
851 this._最後にプレイヤーに渡した一時ファイル名 = Path.Combine( this._作業フォルダパス, Path.GetRandomFileName() );
853 while( File.Exists( this._最後にプレイヤーに渡した一時ファイル名 ) ); // 同一名のファイルが存在してたらもう一度。(まずないだろうが)
857 this._最後にプレイヤーに渡した一時ファイル名,
858 一時ファイルである: true ); // 一時ファイルである場合、「最近使ったファイル一覧」には残されない。
860 #region " SSTサービスを取得する。(必要あれば、SSTビュアープロセスを起動する。)"
862 this._SSTサービスが起動していれば取得する();
866 bool ビュアープロセスを起動する = false;
868 if( null == this._SSTサービス )
870 ビュアープロセスを起動する = true; // サービスが起動してなかったので、必要。
876 this._SSTサービス.GetSoundDelay();
880 ビュアープロセスを起動する = true; // サービスAPIが呼び出せなかったので、必要。
884 // 必要ならビュアープロセスを起動する。
890 Process.Start( this.Config.ViewerPath, "-v" ); // ビュアーオプション付き。
897 // 起動したプロセスの提供するSSTサービスへ接続する。
899 this._SSTファクトリ = null;
900 for( int retry = 0; retry < 10; retry++ ) // 最大10回リトライ。
902 this._SSTサービスが起動していれば取得する();
904 if( null != this._SSTサービス )
905 break; // サービスに接続できた。
908 System.Threading.Thread.Sleep( 500 );
911 if( null == this._SSTサービス )
913 this._Viewer再生関連GUIのEnabledを設定する();
914 return; // サービスへの接続に失敗した。
921 float 遅延ms = this._SSTサービス.GetSoundDelay();
923 if( this.譜面.SSTFormatScore.Header.サウンドデバイス遅延ms != 遅延ms )
925 this.textBoxサウンド遅延ms.Text = 遅延ms.ToString(); // GUI と
926 this.譜面.SSTFormatScore.Header.サウンドデバイス遅延ms = 遅延ms; // 譜面にセットする。
934 this._SSTサービス.ViewerPlay(
935 path: this._最後にプレイヤーに渡した一時ファイル名,
937 drumsSound: ドラム音を発声する );
944 private void _再生を停止する()
946 if( ( this.Config.ViewerPath.Nullまたは空である() ) ||
947 ( false == File.Exists( this.Config.ViewerPath ) ) )
950 // SSTサービスを通じて、演奏を停止する。
952 this._SSTサービスが起動していれば取得する();
954 if( null == this._SSTサービス )
955 return; // SSTサービスが起動していないなら何もしない。
957 this._SSTサービス.ViewerStop();
960 private void _オプションを設定する()
962 using( var dialog = new オプションダイアログ() )
964 // Config の現在の値をダイアログへ反映する。
965 dialog.checkBoxオートフォーカス.CheckState = ( this.Config.AutoFocus ) ? CheckState.Checked : CheckState.Unchecked;
966 dialog.checkBox最近使用したファイル.CheckState = ( this.Config.ShowRecentUsedFiles ) ? CheckState.Checked : CheckState.Unchecked;
967 dialog.numericUpDown最近使用したファイルの最大表示個数.Value = this.Config.MaxOfUsedRecentFiles;
968 dialog.textBoxViewerPath.Text = this.Config.ViewerPath;
970 if( DialogResult.OK == dialog.ShowDialog( this ) )
972 // 決定された値をダイアログから Config に反映する。
973 this.Config.AutoFocus = dialog.checkBoxオートフォーカス.Checked;
974 this.Config.ShowRecentUsedFiles = dialog.checkBox最近使用したファイル.Checked;
975 this.Config.MaxOfUsedRecentFiles = (int) dialog.numericUpDown最近使用したファイルの最大表示個数.Value;
976 this.Config.ViewerPath = dialog.textBoxViewerPath.Text;
978 this._Viewer再生関連GUIのEnabledを設定する();
981 this._ConfigのRecentUsedFilesをファイルメニューへ追加する();
984 this.Config.保存する( Path.Combine( this._ユーザフォルダパス, Properties.Resources.CONFIG_FILE_NAME ) );
988 // 画面を再描画してダイアログのゴミを消す。
992 private void _バージョンを表示する()
994 using( var dialog = new バージョン表示ダイアログ() )
995 dialog.ShowDialog( this );
998 private void _小節長倍率を変更する( int 小節番号 )
1001 int 変更開始小節番号 = 小節番号;
1002 int 変更終了小節番号 = 小節番号;
1004 #region " 変更後の小節長倍率をユーザに入力させる。"
1006 double 現在の小節長倍率 = this.譜面.SSTFormatScore.小節長倍率を取得する( 小節番号 );
1008 using( var dialog = new 小節長倍率入力ダイアログ( 小節番号 ) )
1010 dialog.倍率 = (float) 現在の小節長倍率;
1011 dialog.後続も全部変更する = false;
1013 if( DialogResult.OK != dialog.ShowDialog( this ) ) // キャンセルされたらここで中断。
1016 変更後倍率 = (double) dialog.倍率;
1017 変更終了小節番号 = ( dialog.後続も全部変更する ) ? this.譜面.SSTFormatScore.最大小節番号 : 小節番号;
1024 this.UndoRedo管理.トランザクション記録を開始する();
1026 for( int i = 変更開始小節番号; i <= 変更終了小節番号; i++ )
1028 var 変更前倍率 = this.譜面.SSTFormatScore.小節長倍率を取得する( i );
1030 #region " 新しい小節長倍率を設定する。"
1032 var cell = new UndoRedo.セル<double>(
1034 Undoアクション: ( 変更対象, 変更前, 変更後, 対象小節番号, 任意2 ) => {
1035 this.譜面.SSTFormatScore.小節長倍率を設定する( (int) 対象小節番号, 変更前 );
1038 Redoアクション: ( 変更対象, 変更前, 変更後, 対象小節番号, 任意2 ) => {
1039 this.譜面.SSTFormatScore.小節長倍率を設定する( (int) 対象小節番号, 変更後 );
1048 this.UndoRedo管理.セルを追加する( cell );
1053 #region " チップを移動または削除する。"
1055 int 変化量grid = (int) ( ( 変更後倍率 - 変更前倍率 ) * this.GRID_PER_PART );
1057 for( int j = this.譜面.SSTFormatScore.チップリスト.Count - 1; j >= 0; j-- ) // 削除する場合があるので後ろからカウントする。
1059 var chip = this.譜面.SSTFormatScore.チップリスト[ j ];
1061 // (A) 変更対象の小節内のチップ → 移動なし。カウント変更あり。小節はみ出しチェックあり。
1063 if( chip.小節番号 == i )
1065 #region " (A-a) 小節からはみ出したチップは、削除する。"
1067 int 次小節の先頭位置grid = this.譜面.小節先頭の譜面内絶対位置gridを返す( i ) + (int) ( 変更後倍率 * this.GRID_PER_PART );
1069 if( 次小節の先頭位置grid <= chip.譜面内絶対位置grid )
1071 var chip変更前 = new SSTFormat.チップ( chip );
1073 var cc = new UndoRedo.セル<SSTFormat.チップ>(
1075 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
1076 変更対象.CopyFrom( 変更前 );
1077 this.譜面.SSTFormatScore.チップリスト.Add( 変更対象 );
1078 this.譜面.SSTFormatScore.チップリスト.Sort();
1081 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
1082 this.譜面.SSTFormatScore.チップリスト.Remove( 変更対象 );
1091 this.UndoRedo管理.セルを追加する( cc );
1097 #region " (A-b) 小節からはみ出さなかったチップは、カウント(小節解像度と小節内位置)を変更する。"
1101 // 解像度が小さいと計算結果がおかしくなるので、無条件に解像度を上げる。
1103 int 十分に大きな値 = this.GRID_PER_PART; // 充分に大きい数なら何でもいい。
1104 if( 十分に大きな値 > chip.小節解像度 ) // 何度も乗算されたら値が肥大化しまくるので、制限をかける。
1106 chip.小節解像度 *= 十分に大きな値;
1107 chip.小節内位置 *= 十分に大きな値;
1110 // 小節解像度を変更する。(小節内位置は変更しない。)
1111 chip.小節解像度 = (int) ( 変更後倍率 * chip.小節解像度 );
1117 // (B) 変更対象より先の小節内のチップ → 移動あり。カウントなし。小節はみ出しチェックなし。
1119 else if( i < chip.小節番号 )
1121 #region " チップを _変化量grid ぶん移動する。"
1123 var cc = new UndoRedo.セル<SSTFormat.チップ>(
1125 Undoアクション: ( 変更対象, 変更前, 変更後, _変化量grid, 任意2 ) => {
1126 変更対象.譜面内絶対位置grid -= (int) _変化量grid;
1129 Redoアクション: ( 変更対象, 変更前, 変更後, _変化量grid, 任意2 ) => {
1130 変更対象.譜面内絶対位置grid += (int) _変化量grid;
1139 this.UndoRedo管理.セルを追加する( cc );
1151 this.UndoRedo管理.トランザクション記録を終了する();
1154 this.UndoRedo用GUIのEnabledを設定する();
1161 private void _小節を挿入する( int 挿入前小節番号 )
1163 // 挿入する新しい小節の小節長は、直前の(挿入前小節番号-1 の小節)と同じサイズとする。
1164 double 小節長倍率 = ( 0 < 挿入前小節番号 ) ? this.譜面.SSTFormatScore.小節長倍率を取得する( 挿入前小節番号 - 1 ) : 1.0;
1168 this.UndoRedo管理.トランザクション記録を開始する();
1170 #region " 後方のチップを移動する。"
1172 int 挿入に伴う増加量grid = (int) ( this.GRID_PER_PART * 小節長倍率 );
1174 foreach( var chip in this.譜面.SSTFormatScore.チップリスト )
1176 if( 挿入前小節番号 <= chip.小節番号 )
1178 var cell = new UndoRedo.セル<SSTFormat.チップ>(
1180 Undoアクション: ( 変更対象, 変更前, 変更後, _挿入に伴う増加量grid, 任意2 ) => {
1182 変更対象.譜面内絶対位置grid -= (int) _挿入に伴う増加量grid;
1184 Redoアクション: ( 変更対象, 変更前, 変更後, _挿入に伴う増加量grid, 任意2 ) => {
1186 変更対象.譜面内絶対位置grid += (int) _挿入に伴う増加量grid;
1194 this.UndoRedo管理.セルを追加する( cell );
1201 #region " 後方の小節長倍率を移動する。"
1203 var cc = new UndoRedo.セル<double>(
1205 Undoアクション: ( 変更対象, 変更前, 変更後, _挿入前小節番号, 任意2 ) => {
1206 this.譜面.SSTFormatScore.小節長倍率リスト.RemoveAt( (int) _挿入前小節番号 );
1209 Redoアクション: ( 変更対象, 変更前, 変更後, _挿入前小節番号, 任意2 ) => {
1210 this.譜面.SSTFormatScore.小節長倍率リスト.Insert( (int) _挿入前小節番号, 小節長倍率 );
1219 this.UndoRedo管理.セルを追加する( cc );
1226 this.UndoRedo管理.トランザクション記録を終了する();
1229 this.UndoRedo用GUIのEnabledを設定する();
1235 private void _小節を削除する( int 削除する小節番号 )
1237 double 削除する小節の小節長倍率 = this.譜面.SSTFormatScore.小節長倍率を取得する( 削除する小節番号 );
1242 this.UndoRedo管理.トランザクション記録を開始する();
1244 #region " 削除される小節内のチップをすべて削除する。"
1246 for( int i = this.譜面.SSTFormatScore.チップリスト.Count - 1; i >= 0; i-- )
1248 var chip = this.譜面.SSTFormatScore.チップリスト[ i ];
1250 if( 削除する小節番号 == chip.小節番号 )
1252 var chip変更前 = new SSTFormat.チップ( chip );
1254 var cell = new UndoRedo.セル<SSTFormat.チップ>(
1256 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
1257 変更対象.CopyFrom( 変更前 );
1258 this.譜面.SSTFormatScore.チップリスト.Add( 変更対象 );
1259 this.譜面.SSTFormatScore.チップリスト.Sort();
1261 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
1262 this.譜面.SSTFormatScore.チップリスト.Remove( 変更対象 );
1271 this.UndoRedo管理.セルを追加する( cell );
1278 #region " 削除した小節より後方のチップを、その小節分だけ前に移動する。"
1280 int 削除に伴う減少量grid = (int) ( this.GRID_PER_PART * 削除する小節の小節長倍率 );
1282 foreach( var chip in this.譜面.SSTFormatScore.チップリスト )
1284 if( 削除する小節番号 < chip.小節番号 )
1286 var cell = new UndoRedo.セル<SSTFormat.チップ>(
1288 Undoアクション: ( 変更対象, 変更前, 変更後, _削除に伴う減少量grid, 任意2 ) => {
1290 変更対象.譜面内絶対位置grid += (int) _削除に伴う減少量grid;
1292 Redoアクション: ( 変更対象, 変更前, 変更後, _削除に伴う減少量grid, 任意2 ) => {
1294 変更対象.譜面内絶対位置grid -= (int) _削除に伴う減少量grid;
1302 this.UndoRedo管理.セルを追加する( cell );
1309 #region " 削除した小節を、小節長倍率リストからも削除する。"
1311 var cc = new UndoRedo.セル<double>(
1313 Undoアクション: ( 変更対象, 変更前, 変更後, _削除する小節番号, 任意2 ) => {
1314 this.譜面.SSTFormatScore.小節長倍率リスト.Insert( (int) _削除する小節番号, 変更前 );
1317 Redoアクション: ( 変更対象, 変更前, 変更後, _削除する小節番号, 任意2 ) => {
1318 this.譜面.SSTFormatScore.小節長倍率リスト.RemoveAt( (int) _削除する小節番号 );
1322 変更前の値: 削除する小節の小節長倍率,
1327 this.UndoRedo管理.セルを追加する( cc );
1334 this.UndoRedo管理.トランザクション記録を終了する();
1337 this.UndoRedo用GUIのEnabledを設定する();
1344 private void _小節の先頭へ移動する( int 小節番号 )
1349 else if( 小節番号 > this.譜面.SSTFormatScore.最大小節番号 )
1350 小節番号 = this.譜面.SSTFormatScore.最大小節番号;
1352 // 垂直スクロールバーを移動させると、画面も自動的に移動する。
1353 var bar = this.vScrollBar譜面用垂直スクロールバー;
1354 bar.Value = ( ( bar.Maximum + 1 ) - bar.LargeChange ) - this.譜面.小節先頭の譜面内絶対位置gridを返す( 小節番号 );
1359 #region " アクション補佐メソッド(複数のアクションで共通する処理。) "
1361 private void _エディタを初期化する()
1363 this._編集中のファイル名 = null;
1365 #region " 各種オブジェクトを生成する。"
1368 this.譜面 = new C譜面( this ); // 譜面は、選択・編集モードよりも先に生成すること。
1370 this.UndoRedo管理 = new UndoRedo.管理();
1371 this.選択モード = new 選択モード( this );
1372 this.編集モード = new 編集モード( this );
1373 this.クリップボード = new クリップボード( this );
1381 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1382 this.textBox曲名.Clear();
1384 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1385 this.textBox説明.Clear();
1388 #region " Viewer 再生 "
1390 this._Viewer再生関連GUIのEnabledを設定する();
1395 this._ガイド間隔を変更する( 16 ); // 初期値は 1/16。
1400 this._譜面拡大率を変更する( 1 ); // 初期値は x1.0。
1403 #region " Undo/Redo "
1405 this._次のプロパティ変更がUndoRedoリストに載るようにする();
1406 this.UndoRedo用GUIのEnabledを設定する();
1409 #region " 垂直スクロールバー "
1411 this.vScrollBar譜面用垂直スクロールバー.Minimum = 0;
1412 this.vScrollBar譜面用垂直スクロールバー.Maximum = ( this.譜面.全小節の高さgrid - 1 );
1413 this.vScrollBar譜面用垂直スクロールバー.SmallChange = ( this.GRID_PER_PART / 16 );
1414 this.vScrollBar譜面用垂直スクロールバー.LargeChange = this.GRID_PER_PART;
1415 this.vScrollBar譜面用垂直スクロールバー.Value = this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
1420 this.編集モードに切替えて関連GUIを設定する();
1422 this.未保存である = false;
1425 private DialogResult _未保存なら保存する()
1428 if( false == this.未保存である )
1429 return DialogResult.OK;
1431 // [編集中のデータを保存しますか?] ダイアログを表示。
1432 var result = MessageBox.Show(
1433 Properties.Resources.MSG_編集中のデータを保存しますか,
1434 Properties.Resources.MSG_確認ダイアログのタイトル,
1435 MessageBoxButtons.YesNoCancel,
1436 MessageBoxIcon.Question,
1437 MessageBoxDefaultButton.Button1 );
1440 if( DialogResult.Yes == result )
1443 // 画面を再描画してダイアログを消去する。
1446 return result; // ダイアログの結果を返す。
1449 private string _ファイル保存ダイアログを開いてファイル名を取得する()
1451 DialogResult result;
1454 // ダイアログでファイル名を取得する。
1455 using( var dialog = new SaveFileDialog() {
1457 Filter = "SSTFファイル(*.sstf)|*.sstf",
1459 InitialDirectory = this._作業フォルダパス,
1462 result = dialog.ShowDialog( this );
1463 ファイル名 = dialog.FileName;
1466 // 画面を再描画してダイアログのゴミを消去する。
1469 // キャンセルされたら ""(空文字列)を返す。
1470 if( DialogResult.OK != result )
1473 // ファイルの拡張子を .sstf に変更。
1474 if( 0 == Path.GetExtension( ファイル名 ).Length )
1475 ファイル名 = Path.ChangeExtension( ファイル名, ".sstf" );
1480 private void _ファイルを読み込む( string ファイル名 )
1484 #region " [読み込み中です] ポップアップを表示する。"
1486 var msg = new Popupメッセージ(
1487 Properties.Resources.MSG_読み込み中です + Environment.NewLine +
1488 Properties.Resources.MSG_しばらくお待ち下さい );
1497 this.譜面.曲データファイルを読み込む( ファイル名 );
1499 // 最低でも 10 小節は存在させる。
1500 for( int n = this.譜面.SSTFormatScore.最大小節番号 + 1; n < 9; n++ )
1503 string 読み込み時の拡張子 = Path.GetExtension( ファイル名 ).ToLower();
1504 this._編集中のファイル名 = Path.ChangeExtension( Path.GetFileName( ファイル名 ), ".sstf" ); // 読み込んだファイルの拡張子を .sstf に変換。
1505 this._作業フォルダパス = Path.GetDirectoryName( ファイル名 );
1507 // 読み込んだファイルを [ファイル]メニューの最近使ったファイル一覧に追加する。
1508 this.Config.ファイルを最近使ったファイルの一覧に追加する( Path.Combine( this._作業フォルダパス, this._編集中のファイル名 ) );
1509 this._ConfigのRecentUsedFilesをファイルメニューへ追加する();
1512 譜面.SSTFormatScore.背景動画ファイル名 =
1513 ( from file in Directory.GetFiles( Path.GetDirectoryName( this._作業フォルダパス ) )
1514 where SSTFormat.スコア.背景動画のデフォルト拡張子s.Any( 拡張子名 => ( Path.GetExtension( file ).ToLower() == 拡張子名 ) )
1515 select file ).FirstOrDefault(); // 複数あったら、最初に見つけたほうを採用。1つも見つからなければ null。
1516 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1517 this.textBox曲名.Text = 譜面.SSTFormatScore.Header.曲名;
1518 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1519 this.textBox説明.Text = 譜面.SSTFormatScore.Header.説明文;
1520 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1521 this.textBox背景動画.Text = Path.GetFileName( 譜面.SSTFormatScore.背景動画ファイル名 );
1522 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1523 this.textBoxメモ.Text = ( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 0 ) ) ? this.譜面.SSTFormatScore.dicメモ[ 0 ] : "";
1524 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
1525 this.textBoxサウンド遅延ms.Text = this.譜面.SSTFormatScore.Header.サウンドデバイス遅延ms.ToString();
1527 // ウィンドウのタイトルバーの表示変更(str編集中のファイル名 が確定した後に)
1528 this.未保存である = true; // 以前の状態によらず、確実に更新するようにする。
1530 // sstf 以外を読み込んだ場合は、未保存状態のままとする。
1531 if( 読み込み時の拡張子.ToLower() == ".sstf" )
1532 this.未保存である = false;
1534 catch( InvalidDataException )
1537 Properties.Resources.MSG_対応していないファイルです,
1538 Properties.Resources.MSG_確認ダイアログのタイトル,
1539 MessageBoxButtons.OK );
1542 #region "「読み込み中です」ポップアップを閉じる。 "
1548 // 最後に、ダイアログのゴミなどを消すために画面を再描画する。
1552 private enum タブ種別 : int { 基本情報 = 0 }
1553 private void _タブを選択する( タブ種別 eタブ種別 )
1555 this.tabControl情報タブコンテナ.SelectedIndex = (int) eタブ種別;
1558 private void _次のプロパティ変更がUndoRedoリストに載らないようにする()
1560 UndoRedo.管理.UndoRedoした直後である = true;
1562 private void _次のプロパティ変更がUndoRedoリストに載るようにする()
1564 UndoRedo.管理.UndoRedoした直後である = false;
1567 private void _垂直スクロールバーと譜面の上下位置を調整する()
1569 var bar = this.vScrollBar譜面用垂直スクロールバー;
1570 var box = this.pictureBox譜面パネル;
1571 var panel2 = this.splitContainer分割パネルコンテナ.Panel2;
1573 // 譜面パネルの長さをパネルに合わせて調整する。
1574 box.ClientSize = new Size(
1575 box.ClientSize.Width,
1576 panel2.ClientSize.Height - box.Location.Y );
1578 // 現在のバーの位置を割合で記憶する。
1579 var bar率 = (double) bar.Value / (double) ( bar.Maximum - bar.Minimum );
1581 // 新しい値域を設定した後、バーの位置を記憶しておいた割合で設定。
1583 bar.Maximum = this.譜面.全小節の高さgrid - 1;
1584 bar.Value = (int) ( ( bar.Maximum - bar.Minimum ) * bar率 );
1586 // 譜面長さが画面長さより短いなら、スクロールバーを表示しない。
1587 bar.Enabled = ( bar.Maximum > bar.LargeChange ) ? true : false;
1590 private void _Viewer再生関連GUIのEnabledを設定する()
1592 bool 各GUIは有効 = false;
1594 if( null != this._SSTサービス )
1596 // (A) SSTサービスが起動しているなら、各GUIは有効。
1599 else if( File.Exists( this.Config.ViewerPath ) )
1601 // (B) SSTサービスが起動していなくても、Viewer が存在するなら、各GUIは有効。
1606 // (C) SSTサービスが起動しておらず、Viewer も存在しないなら、各GUIは無効。
1610 this.toolStripButton先頭から再生.Enabled = 各GUIは有効;
1611 this.toolStripButton現在位置から再生.Enabled = 各GUIは有効;
1612 this.toolStripButton現在位置からBGMのみ再生.Enabled = 各GUIは有効;
1613 this.toolStripButton再生停止.Enabled = 各GUIは有効;
1615 this.toolStripMenuItem先頭から再生.Enabled = 各GUIは有効;
1616 this.toolStripMenuItem現在位置から再生.Enabled = 各GUIは有効;
1617 this.toolStripMenuItem現在位置からBGMのみ再生.Enabled = 各GUIは有効;
1618 this.toolStripMenuItem再生停止.Enabled = 各GUIは有効;
1621 private void _ConfigのRecentUsedFilesをファイルメニューへ追加する()
1623 #region " [ファイル] メニューから、[最近使ったファイルの一覧] をクリアする。"
1625 for( int i = 0; i < this.toolStripMenuItemファイル.DropDownItems.Count; i++ )
1627 var item = this.toolStripMenuItemファイル.DropDownItems[ i ];
1629 // ↓削除したくないサブメニューの一覧。これ以外のサブメニュー項目はすべて削除する。
1630 if( item != this.toolStripMenuItem新規作成 &&
1631 item != this.toolStripMenuItem開く &&
1632 item != this.toolStripMenuItem上書き保存 &&
1633 item != this.toolStripMenuItem名前を付けて保存 &&
1634 item != this.toolStripSeparator1 &&
1635 item != this.toolStripMenuItem終了 )
1637 this.toolStripMenuItemファイル.DropDownItems.Remove( item );
1638 i = -1; // 要素数が変わったので列挙しなおし。RemoveAll() はないのか。
1644 if( ( false == this.Config.ShowRecentUsedFiles ) || // 表示しない or
1645 ( 0 == this.Config.RecentUsedFiles.Count ) ) // 履歴が 0 件
1648 #region " Config が持つ履歴にそって、[ファイル] メニューにサブメニュー項目リストを追加する(ただし最大表示数まで)。"
1650 // [File] のサブメニューリストに項目が1つでもある場合は、履歴サブメニュー項目を追加する前に、[終了] の下にセパレータを入れる。手動で。
1651 bool セパレータの追加がまだ = true;
1653 // すべての [最近使ったファイル] について...
1654 for( int i = 0; i < this.Config.RecentUsedFiles.Count; i++ )
1657 if( this.Config.MaxOfUsedRecentFiles <= i )
1660 // ファイルパスを、サブメニュー項目として [ファイル] メニューに追加する。
1661 string ファイルパス = this.Config.RecentUsedFiles[ i ];
1662 if( string.IsNullOrEmpty( ファイルパス ) )
1665 // セパレータの追加がまだなら追加する。
1668 this.toolStripMenuItemファイル.DropDownItems.Add( new ToolStripSeparator() { Size = this.toolStripSeparator1.Size } );
1669 セパレータの追加がまだ = false;
1672 // ToolStripMenuItem を手動で作って [ファイル] のサブメニューリストに追加する。
1673 var item = new ToolStripMenuItem() {
1674 Name = "最近使ったファイル" + i,
1675 Size = this.toolStripMenuItem終了.Size,
1676 Text = "&" + i + " " + ファイルパス,
1678 item.Click += new EventHandler( this.toolStripMenuItem最近使ったファイル_Click );
1679 this.toolStripMenuItemファイル.DropDownItems.Add( item );
1681 // 追加したファイルが既に存在していないなら項目を無効化(グレー表示)する。
1682 if( false == File.Exists( ファイルパス ) )
1683 item.Enabled = false;
1689 private Point _現在のマウス位置を譜面パネル内座標pxに変換して返す()
1691 return this.pictureBox譜面パネル.PointToClient( new Point( Cursor.Position.X, Cursor.Position.Y ) );
1694 private void _現在のチップ音量をツールバーに表示する()
1696 this.toolStripLabel音量.Text = ( this._音量toラベル.ContainsKey( this.現在のチップ音量 ) ) ? this._音量toラベル[ this.現在のチップ音量 ] : @"???";
1699 private void _SSTサービスが起動していれば取得する()
1702 if( null == this._SSTファクトリ )
1704 this._SSTファクトリ = new ChannelFactory<SST.IStrokeStyleTService>( new NetNamedPipeBinding( NetNamedPipeSecurityMode.None ) );
1705 this._SSTサービスチャンネル = null;
1706 this._SSTサービス = null;
1710 if( ( null == this._SSTサービス ) || ( null == this._SSTサービスチャンネル ) )
1714 this._SSTサービス = this._SSTファクトリ.CreateChannel( new EndpointAddress( "net.pipe://localhost/StrokeStyleT/Viewer" ) );
1715 this._SSTサービスチャンネル = this._SSTサービス as IClientChannel; // サービスとチャンネルは同じオブジェクト。
1716 this._SSTサービスチャンネル.Open();
1717 this._SSTサービスチャンネル.Closed += ( sender, e ) => {
1718 this._SSTサービスチャンネル = null;
1719 this._SSTサービス = null;
1725 this._SSTサービス = null;
1726 this._SSTサービスチャンネル = null;
1736 #region " メインフォーム イベント "
1738 protected void メインフォーム_DragDrop( object sender, DragEventArgs e )
1740 string[] data = (string[]) e.Data.GetData( DataFormats.FileDrop );
1742 if( 1 <= data.Length )
1743 this._指定されたファイルを開く( data[ 0 ] ); // Dropされたファイルが複数個あっても、先頭のファイルだけを有効とする。
1745 protected void メインフォーム_DragEnter( object sender, DragEventArgs e )
1747 if( e.Data.GetDataPresent( DataFormats.FileDrop ) )
1749 e.Effect = DragDropEffects.Copy; // ファイルならコピーと見なす(カーソルがコピー型になる)
1753 e.Effect = DragDropEffects.None; // ファイルじゃないなら無視(カーソル変化なし)
1756 protected void メインフォーム_FormClosing( object sender, FormClosingEventArgs e )
1758 if( DialogResult.Cancel == this._未保存なら保存する() )
1764 this._アプリの終了処理を行う();
1770 #region " メニューバー イベント [File] "
1772 protected void toolStripMenuItem新規作成_Click( object sender, EventArgs e )
1776 protected void toolStripMenuItem開く_Click( object sender, EventArgs e )
1780 protected void toolStripMenuItem上書き保存_Click( object sender, EventArgs e )
1784 protected void toolStripMenuItem名前を付けて保存_Click( object sender, EventArgs e )
1788 protected void toolStripMenuItem終了_Click( object sender, EventArgs e )
1792 protected void toolStripMenuItem最近使ったファイル_Click( object sender, EventArgs e )
1794 // ※このイベントハンドラに対応する「toolStripMenuItem最近使ったファイル」というアイテムはデザイナにはないので注意。
1795 // 最近使ったファイルをFileメニューへ追加する際に、手動で作って追加したアイテムに対するハンドラである。
1797 this._指定されたファイルを開く( ( (ToolStripMenuItem) sender ).Text.Substring( 3 ) );
1802 #region " メニューバー イベント [Edit] "
1804 protected void toolStripMenuItem元に戻す_Click( object sender, EventArgs e )
1808 protected void toolStripMenuItemやり直す_Click( object sender, EventArgs e )
1812 protected void toolStripMenuItem切り取り_Click( object sender, EventArgs e )
1816 protected void toolStripMenuItemコピー_Click( object sender, EventArgs e )
1820 protected void toolStripMenuItem貼り付け_Click( object sender, EventArgs e )
1822 var マウスの位置 = this._現在のマウス位置を譜面パネル内座標pxに変換して返す();
1824 // (A) マウスが譜面上になかった → 表示領域下辺から貼り付ける。
1825 if( ( ( 0 > マウスの位置.X ) || ( 0 > マウスの位置.Y ) ) ||
1826 ( ( マウスの位置.X > this.譜面パネルサイズ.Width ) || ( マウスの位置.Y > this.譜面パネルサイズ.Height ) ) )
1828 this._貼り付ける( this.譜面.譜面表示下辺の譜面内絶対位置grid );
1830 // (B) マウスが譜面上にある → そこから貼り付ける。
1833 this._貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
1836 protected void toolStripMenuItem削除_Click( object sender, EventArgs e )
1840 protected void toolStripMenuItemすべて選択_Click( object sender, EventArgs e )
1844 protected void toolStripMenuItem選択モード_Click( object sender, EventArgs e )
1846 this.選択モードに切替えて関連GUIを設定する();
1848 protected void toolStripMenuItem編集モード_Click( object sender, EventArgs e )
1850 this.編集モードに切替えて関連GUIを設定する();
1852 protected void toolStripMenuItemモード切替え_Click( object sender, EventArgs e )
1856 this.編集モードに切替えて関連GUIを設定する();
1860 this.選択モードに切替えて関連GUIを設定する();
1863 protected void toolStripMenuItem検索_Click( object sender, EventArgs e )
1870 #region " メニューバー イベント [View] "
1872 protected void toolStripMenuItemガイド間隔4分_Click( object sender, EventArgs e )
1874 this._ガイド間隔を変更する( 4 );
1876 protected void toolStripMenuItemガイド間隔6分_Click( object sender, EventArgs e )
1878 this._ガイド間隔を変更する( 6 );
1880 protected void toolStripMenuItemガイド間隔8分_Click( object sender, EventArgs e )
1882 this._ガイド間隔を変更する( 8 );
1884 protected void toolStripMenuItemガイド間隔12分_Click( object sender, EventArgs e )
1886 this._ガイド間隔を変更する( 12 );
1888 protected void toolStripMenuItemガイド間隔16分_Click( object sender, EventArgs e )
1890 this._ガイド間隔を変更する( 16 );
1892 protected void toolStripMenuItemガイド間隔24分_Click( object sender, EventArgs e )
1894 this._ガイド間隔を変更する( 24 );
1896 protected void toolStripMenuItemガイド間隔32分_Click( object sender, EventArgs e )
1898 this._ガイド間隔を変更する( 32 );
1900 protected void toolStripMenuItemガイド間隔48分_Click( object sender, EventArgs e )
1902 this._ガイド間隔を変更する( 48 );
1904 protected void toolStripMenuItemガイド間隔64分_Click( object sender, EventArgs e )
1906 this._ガイド間隔を変更する( 64 );
1908 protected void toolStripMenuItemガイド間隔128分_Click( object sender, EventArgs e )
1910 this._ガイド間隔を変更する( 128 );
1912 protected void toolStripMenuItemガイド間隔フリー_Click( object sender, EventArgs e )
1914 this._ガイド間隔を変更する( 0 );
1916 protected void toolStripMenuItemガイド間隔拡大_Click( object sender, EventArgs e )
1920 protected void toolStripMenuItemガイド間隔縮小_Click( object sender, EventArgs e )
1927 #region " メニューバー イベント [Play] "
1929 protected void toolStripMenuItem先頭から再生_Click( object sender, EventArgs e )
1933 protected void toolStripMenuItem現在位置から再生_Click( object sender, EventArgs e )
1937 protected void toolStripMenuItem現在位置からBGMのみ再生_Click( object sender, EventArgs e )
1939 this._現在位置からBGMのみ再生する();
1941 protected void toolStripMenuItem再生停止_Click( object sender, EventArgs e )
1948 #region " メニューバー イベント [Tool] "
1950 protected void toolStripMenuItemオプション_Click( object sender, EventArgs e )
1957 #region " メニューバー イベント [Help] "
1959 protected void toolStripMenuItemバージョン_Click( object sender, EventArgs e )
1966 #region " ツールバー イベント "
1968 protected void toolStripButton新規作成_Click( object sender, EventArgs e )
1972 protected void toolStripButton開く_Click( object sender, EventArgs e )
1976 protected void toolStripButton上書き保存_Click( object sender, EventArgs e )
1980 protected void toolStripButton切り取り_Click( object sender, EventArgs e )
1984 protected void toolStripButtonコピー_Click( object sender, EventArgs e )
1988 protected void toolStripButton貼り付け_Click( object sender, EventArgs e )
1990 var マウスの位置 = this._現在のマウス位置を譜面パネル内座標pxに変換して返す();
1992 // (A) マウスが譜面上になかった → 表示領域下辺から貼り付ける。
1993 if( ( ( マウスの位置.X < 0 ) || ( マウスの位置.Y < 0 ) ) ||
1994 ( ( マウスの位置.X > this.譜面パネルサイズ.Width ) || ( マウスの位置.Y > this.譜面パネルサイズ.Height ) ) )
1996 this._貼り付ける( this.譜面.譜面表示下辺の譜面内絶対位置grid );
1998 // (B) マウスが譜面上にある → そこから貼り付ける。
2001 this._貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
2004 protected void toolStripButton削除_Click( object sender, EventArgs e )
2009 protected void toolStripButton元に戻す_Click( object sender, EventArgs e )
2013 protected void toolStripButtonやり直す_Click( object sender, EventArgs e )
2018 protected void toolStripComboBox譜面拡大率_SelectedIndexChanged( object sender, EventArgs e )
2020 this._譜面拡大率を変更する( this.toolStripComboBox譜面拡大率.SelectedIndex + 1 );
2022 protected void toolStripComboBoxガイド間隔_SelectedIndexChanged( object sender, EventArgs e )
2024 switch( this.toolStripComboBoxガイド間隔.SelectedIndex )
2027 this._ガイド間隔を変更する( 4 );
2031 this._ガイド間隔を変更する( 6 );
2035 this._ガイド間隔を変更する( 8 );
2039 this._ガイド間隔を変更する( 12 );
2043 this._ガイド間隔を変更する( 16 );
2047 this._ガイド間隔を変更する( 24 );
2051 this._ガイド間隔を変更する( 32 );
2055 this._ガイド間隔を変更する( 48 );
2059 this._ガイド間隔を変更する( 64 );
2063 this._ガイド間隔を変更する( 128 );
2067 this._ガイド間隔を変更する( 0 );
2072 protected void toolStripButton選択モード_Click( object sender, EventArgs e )
2076 protected void toolStripButton編集モード_Click( object sender, EventArgs e )
2081 protected void toolStripButton先頭から再生_Click( object sender, EventArgs e )
2085 protected void toolStripButton現在位置から再生_Click( object sender, EventArgs e )
2089 protected void toolStripButton現在位置からBGMのみ再生_Click( object sender, EventArgs e )
2091 this._現在位置からBGMのみ再生する();
2093 protected void toolStripButton再生停止_Click( object sender, EventArgs e )
2098 protected void toolStripButton音量Down_Click( object sender, EventArgs e )
2100 int 新音量 = this.現在のチップ音量 - 1;
2101 this.現在のチップ音量 = ( 新音量 < メインフォーム.最小音量 ) ? メインフォーム.最小音量 : 新音量;
2103 this._現在のチップ音量をツールバーに表示する();
2105 protected void toolStripButton音量UP_Click( object sender, EventArgs e )
2107 int 新音量 = this.現在のチップ音量 + 1;
2108 this.現在のチップ音量 = ( 新音量 > メインフォーム.最大音量 ) ? メインフォーム.最大音量 : 新音量;
2110 this._現在のチップ音量をツールバーに表示する();
2115 #region " 分割パネルコンテナ、譜面パネル、スクロールバー イベント "
2117 protected void pictureBox譜面パネル_MouseClick( object sender, MouseEventArgs e )
2120 this.pictureBox譜面パネル.Focus();
2122 // 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
2125 this.選択モード.MouseClick( e );
2129 this.編集モード.MouseClick( e );
2132 protected void pictureBox譜面パネル_MouseDown( object sender, MouseEventArgs e )
2134 // 選択モードオブジェクトへ処理を引き継ぐ。
2136 this.選択モード.MouseDown( e );
2138 protected void pictureBox譜面パネル_MouseEnter( object sender, EventArgs e )
2140 // オートフォーカスが有効の場合、譜面にマウスが入ったら譜面がフォーカスを得る。"
2141 if( this.Config.AutoFocus )
2142 this.pictureBox譜面パネル.Focus();
2144 protected void pictureBox譜面パネル_MouseLeave( object sender, EventArgs e )
2146 // 編集モードオブジェクトへ処理を引き継ぐ。
2148 this.編集モード.MouseLeave( e );
2150 protected void pictureBox譜面パネル_MouseMove( object sender, MouseEventArgs e )
2152 // 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
2155 this.選択モード.MouseMove( e );
2159 this.編集モード.MouseMove( e );
2162 protected void pictureBox譜面パネル_Paint( object sender, PaintEventArgs e )
2164 if( false == this.初期化完了 )
2165 return; // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2167 #region " 小節数が変わってたら、スクロールバーの値域を調整する。"
2169 int 全譜面の高さgrid = this.譜面.全小節の高さgrid;
2171 if( this.vScrollBar譜面用垂直スクロールバー.Maximum != 全譜面の高さgrid - 1 ) // 小節数が変わっている
2173 // 譜面の高さ(grid)がどれだけ変わったか?
2174 int 増加分grid = ( 全譜面の高さgrid - 1 ) - this.vScrollBar譜面用垂直スクロールバー.Maximum;
2176 #region " スクロールバーの状態を新しい譜面の高さに合わせる。"
2179 int value = this.vScrollBar譜面用垂直スクロールバー.Value; // 次の式で Maximum が Value より小さくなると例外が発生するので、
2180 this.vScrollBar譜面用垂直スクロールバー.Value = 0; // Value のバックアップを取っておいて、ひとまず 0 にする。
2181 this.vScrollBar譜面用垂直スクロールバー.Maximum = 全譜面の高さgrid - 1;
2183 int newValue = value + 増加分grid;
2185 // オーバーフローしないようクリッピングする。
2188 this.vScrollBar譜面用垂直スクロールバー.Value = 0;
2190 else if( ( this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange ) <= newValue )
2192 this.vScrollBar譜面用垂直スクロールバー.Value = this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
2196 this.vScrollBar譜面用垂直スクロールバー.Value = newValue;
2201 #region " 譜面表示下辺の位置を更新する。"
2203 this.譜面.譜面表示下辺の譜面内絶対位置grid =
2204 ( ( this.vScrollBar譜面用垂直スクロールバー.Maximum - this.vScrollBar譜面用垂直スクロールバー.LargeChange ) + 1 ) - this.vScrollBar譜面用垂直スクロールバー.Value;
2212 this.譜面.描画する( e.Graphics, this.pictureBox譜面パネル );
2216 // 選択・編集モードオブジェクトのいずれかへ処理を引き継ぐ。
2219 this.選択モード.Paint( e );
2223 this.編集モード.Paint( e );
2226 protected void pictureBox譜面パネル_PreviewKeyDown( object sender, PreviewKeyDownEventArgs e )
2228 if( Keys.Prior == e.KeyCode )
2230 #region " PageUp → 垂直つまみを移動させる。あとはこの移動で生じる ChangedValue イベントで処理。"
2232 int 移動すべき数grid = -this.GRID_PER_PART;
2233 int 新しい位置 = this.vScrollBar譜面用垂直スクロールバー.Value + 移動すべき数grid;
2234 int 最小値 = this.vScrollBar譜面用垂直スクロールバー.Minimum;
2235 int 最大値 = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
2241 else if( 新しい位置 > 最大値 )
2245 this.vScrollBar譜面用垂直スクロールバー.Value = 新しい位置;
2249 else if( Keys.Next == e.KeyCode )
2251 #region " PageDown → 垂直つまみを移動させる。あとはこの移動で生じる ChangedValue イベントで処理。"
2253 int 移動すべき数grid = this.GRID_PER_PART;
2254 int 新しい位置 = this.vScrollBar譜面用垂直スクロールバー.Value + 移動すべき数grid;
2255 int 最小値 = this.vScrollBar譜面用垂直スクロールバー.Minimum;
2256 int 最大値 = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
2262 else if( 新しい位置 > 最大値 )
2266 this.vScrollBar譜面用垂直スクロールバー.Value = 新しい位置;
2272 // 編集モードオブジェクトへ処理を引き継ぐ。
2274 this.編集モード.PreviewKeyDown( e );
2278 protected void splitContainer分割パネルコンテナ_MouseWheel( object sender, MouseEventArgs e )
2280 if( false == this.初期化完了 )
2281 return; // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2283 #region " 移動量に対応する grid だけ垂直つまみを移動させる。あとはこの移動で生じる ChangedValue イベントで処理する。"
2288 // e.Delta は、スクロールバーを下へ動かしたいときに負、上へ動かしたいときに正となる。
2289 int 移動すべき行数 = ( -e.Delta * SystemInformation.MouseWheelScrollLines ) / 120;
2292 int 移動すべき数grid = 移動すべき行数 * ( this.GRID_PER_PART / 8 );
2294 // スクロールバーのつまみを移動する。
2295 int 新しい位置 = this.vScrollBar譜面用垂直スクロールバー.Value + 移動すべき数grid;
2296 int 最小値 = this.vScrollBar譜面用垂直スクロールバー.Minimum;
2297 int 最大値 = ( this.vScrollBar譜面用垂直スクロールバー.Maximum + 1 ) - this.vScrollBar譜面用垂直スクロールバー.LargeChange;
2303 else if( 新しい位置 > 最大値 )
2307 this.vScrollBar譜面用垂直スクロールバー.Value = 新しい位置;
2311 protected void splitContainer分割パネルコンテナ_Panel2_SizeChanged( object sender, EventArgs e )
2313 if( false == this.初期化完了 )
2314 return; // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2316 this._垂直スクロールバーと譜面の上下位置を調整する();
2318 protected void splitContainer分割パネルコンテナ_Panel2_Paint( object sender, PaintEventArgs e )
2320 if( false == this.初期化完了 )
2321 return; // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2324 var メモ領域左上隅の位置 = new PointF() {
2325 X = this.譜面.レーンの合計幅px,
2326 Y = this.pictureBox譜面パネル.Location.Y,
2329 #region " 見出し<小節メモ>を描画する。"
2331 g.DrawString( Properties.Resources.MSG_小節メモ, this._メモ用フォント, Brushes.White, PointF.Add( メモ領域左上隅の位置, new Size( 24, -24 )/*マージン*/ ) );
2334 #region " 小節メモを描画する。"
2337 // グリッド値は 上辺>下辺 なので注意。
2338 int パネル下辺grid = this.譜面.譜面表示下辺の譜面内絶対位置grid;
2339 int パネル上辺grid = パネル下辺grid + ( this.pictureBox譜面パネル.ClientSize.Height * this.GRID_PER_PIXEL );
2340 int 開始小節番号 = this.譜面.譜面表示下辺に位置する小節番号;
2342 for( int 小節番号 = 開始小節番号; 小節番号 <= this.譜面.SSTFormatScore.最大小節番号; 小節番号++ )
2344 int 小節の下辺grid = this.譜面.小節先頭の譜面内絶対位置gridを返す( 小節番号 );
2345 int 小節の上辺grid = 小節の下辺grid + this.譜面.小節長をグリッドで返す( 小節番号 );
2347 if( 小節の下辺grid > パネル上辺grid )
2348 break; // 小節が画面上方にはみ出し切ってしまったらそこで終了。
2350 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2352 string メモ = this.譜面.SSTFormatScore.dicメモ[ 小節番号 ];
2354 string[] lines = メモ.Split( new string[] { Environment.NewLine }, StringSplitOptions.None );
2355 int 行数 = lines.Length;
2357 var メモの位置 = new PointF() {
2358 X = メモ領域左上隅の位置.X + 4, // + 4 はマージン
2359 Y = メモ領域左上隅の位置.Y + ( パネル上辺grid - 小節の下辺grid ) / this.GRID_PER_PIXEL - ( 行数 * 16 ), // 9pt = だいたい16px
2361 g.DrawString( メモ, this._メモ用フォント, Brushes.White, メモの位置 );
2368 protected void vScrollBar譜面用垂直スクロールバー_ValueChanged( object sender, EventArgs e )
2370 if( false == this.初期化完了 )
2371 return; // 初期化が終わってないのに呼び出されることがあるので、その場合は無視。
2373 var bar = vScrollBar譜面用垂直スクロールバー;
2378 this.譜面.譜面表示下辺の譜面内絶対位置grid = ( ( bar.Maximum + 1 ) - bar.LargeChange ) - bar.Value;
2380 // 編集モードの場合、カーソルのgrid位置を再計算。
2383 this.編集モード.MouseMove(
2384 new MouseEventArgs( MouseButtons.None, 0, this.編集モード.現在のチップカーソル領域.X, this.編集モード.現在のチップカーソル領域.Y, 0 ) );
2388 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2389 this.numericUpDownメモ用小節番号.Value = this.譜面.カレントラインに位置する小節番号;
2390 this._次のプロパティ変更がUndoRedoリストに載るようにする();
2393 this.splitContainer分割パネルコンテナ.Panel2.Refresh();
2399 #region " 譜面右メニュー イベント "
2401 protected void toolStripMenuItem選択チップの切り取り_Click( object sender, EventArgs e )
2405 protected void toolStripMenuItem選択チップのコピー_Click( object sender, EventArgs e )
2409 protected void toolStripMenuItem選択チップの貼り付け_Click( object sender, EventArgs e )
2411 // メニューが開かれたときのマウスの座標を取得。
2412 // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2413 var マウスの位置 = this._選択モードでコンテクストメニューを開いたときのマウスの位置;
2415 if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2416 return; // クリックされた場所にレーンがないなら無視。
2419 this._貼り付ける( this.譜面.譜面パネル内Y座標pxにおける譜面内絶対位置gridをガイド幅単位で返す( マウスの位置.Y ) );
2421 protected void toolStripMenuItem選択チップの削除_Click( object sender, EventArgs e )
2425 protected void toolStripMenuItemすべてのチップの選択_Click( object sender, EventArgs e )
2427 // 編集モードなら強制的に選択モードにする。
2429 this.選択モードに切替えて関連GUIを設定する();
2432 this.選択モード.全チップを選択する();
2434 protected void toolStripMenuItem小節長変更_Click( object sender, EventArgs e )
2436 // メニューが開かれたときのマウスの座標を取得。
2437 // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2438 var マウスの位置 = this._選択モードでコンテクストメニューを開いたときのマウスの位置;
2440 if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2441 return; // クリックされた場所にレーンがないなら無視。
2444 this._小節長倍率を変更する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
2446 protected void toolStripMenuItem小節の挿入_Click( object sender, EventArgs e )
2448 // メニューが開かれたときのマウスの座標を取得。
2449 // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2450 var マウスの位置 = this._選択モードでコンテクストメニューを開いたときのマウスの位置;
2452 if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2453 return; // クリックされた場所にレーンがないなら無視。
2456 this._小節を挿入する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
2458 protected void toolStripMenuItem小節の削除_Click( object sender, EventArgs e )
2460 // メニューが開かれたときのマウスの座標を取得。
2461 // ※メニューは必ずマウス位置を左上にして表示されるとは限らないため、メニューの表示位置からは取得しないこと。
2462 var マウスの位置 = this._選択モードでコンテクストメニューを開いたときのマウスの位置;
2464 if( this.譜面.譜面パネル内X座標pxにある編集レーンを返す( マウスの位置.X ) == 編集レーン種別.Unknown )
2465 return; // クリックされた場所にレーンがないなら無視。
2468 this._小節を削除する( this.譜面.譜面パネル内Y座標pxにおける小節番号を返す( マウスの位置.Y ) );
2473 #region " 基本情報タブ イベント "
2475 protected void textBox曲名_TextChanged( object sender, EventArgs e )
2477 #region " この変更が Undo/Redo したことによるものではない場合、UndoRedoセルを追加 or 修正する。"
2479 if( false == UndoRedo.管理.UndoRedoした直後である )
2482 var cell = this.UndoRedo管理.Undoするセルを取得して返す_見るだけ();
2484 if( ( null != cell ) && cell.所有権がある( this.textBox曲名 ) )
2486 // (A) 所有者である → 最新のセルの "変更後の値" を現在のコントロールの値に更新する。
2487 ( (UndoRedo.セル<string>) cell ).変更後の値 = this.textBox曲名.Text;
2491 // (B) 所有者ではない → 以下のようにセルを新規追加する。
2494 // "所有者ID" ← 対象となるコンポーネントオブジェクト
2495 var cc = new UndoRedo.セル<string>(
2496 所有者ID: this.textBox曲名,
2497 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2498 this._タブを選択する( タブ種別.基本情報 );
2499 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2500 this.textBox曲名.Text = 変更前;
2501 this.textBox曲名.Focus();
2503 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2504 this._タブを選択する( タブ種別.基本情報 );
2505 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2506 this.textBox曲名.Text = 変更後;
2507 this.textBox曲名.Focus();
2510 変更前の値: this.textBox曲名_以前の値,
2511 変更後の値: this.textBox曲名.Text,
2515 this.UndoRedo管理.セルを追加する( cc );
2518 this.UndoRedo用GUIのEnabledを設定する();
2524 this.textBox曲名_以前の値 = this.textBox曲名.Text; // 以前の値 ← 現在の値
2525 UndoRedo.管理.UndoRedoした直後である = false;
2529 譜面.SSTFormatScore.Header.曲名 = this.textBox曲名.Text;
2531 protected void textBox曲名_Leave( object sender, EventArgs e )
2533 // 最新の UndoRedoセル の所有権を放棄する。
2534 this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBox曲名 );
2536 private string textBox曲名_以前の値 = "";
2538 protected void textBox説明_TextChanged( object sender, EventArgs e )
2540 #region " この変更が Undo/Redo したことによるものではない場合、UndoRedoセルを追加 or 修正する。"
2542 if( false == UndoRedo.管理.UndoRedoした直後である )
2545 var cell = this.UndoRedo管理.Undoするセルを取得して返す_見るだけ();
2546 if( ( null != cell ) && cell.所有権がある( this.textBox説明 ) )
2548 // (A) 所有者である → 最新のセルの "変更後の値" を現在のコントロールの値に更新。
2550 ( (UndoRedo.セル<string>) cell ).変更後の値 = this.textBox説明.Text;
2554 // (B) 所有者ではない → 以下のようにセルを新規追加する。
2557 // "所有者ID" ← 対象となるコンポーネントオブジェクト
2558 var cc = new UndoRedo.セル<string>(
2559 所有者ID: this.textBox説明,
2560 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2561 this._タブを選択する( タブ種別.基本情報 );
2562 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2563 this.textBox説明.Text = 変更前;
2564 this.textBox説明.Focus();
2566 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2567 this._タブを選択する( タブ種別.基本情報 );
2568 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2569 this.textBox説明.Text = 変更後;
2570 this.textBox説明.Focus();
2573 変更前の値: this.textBox説明_以前の値,
2574 変更後の値: this.textBox説明.Text,
2578 this.UndoRedo管理.セルを追加する( cc );
2581 this.UndoRedo用GUIのEnabledを設定する();
2587 this.textBox説明_以前の値 = this.textBox説明.Text; // 以前の値 ← 現在の値
2588 UndoRedo.管理.UndoRedoした直後である = false;
2592 譜面.SSTFormatScore.Header.説明文 = this.textBox説明.Text;
2594 protected void textBox説明_Leave( object sender, EventArgs e )
2596 // 最新 UndoRedoセル の所有権を放棄する。
2597 this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBox説明 );
2599 private string textBox説明_以前の値 = "";
2601 protected void textBoxメモ_TextChanged( object sender, EventArgs e )
2603 #region " この変更が Undo/Redo したことによるものではない場合、UndoRedoセルを追加or修正する。"
2605 if( !UndoRedo.管理.UndoRedoした直後である )
2609 UndoRedo.セルBase cell = this.UndoRedo管理.Undoするセルを取得して返す_見るだけ();
2611 if( ( cell != null ) && cell.所有権がある( this.textBoxメモ ) )
2613 // (Yes) 最新のセルの "変更後の値" を <現在の値> に更新。
2615 ( (UndoRedo.セル<string>) cell ).変更後の値 = this.textBoxメモ.Text;
2622 // "所有者ID" = 対象となるコンポーネントオブジェクト
2624 var cc = new UndoRedo.セル<string>(
2625 所有者ID: this.textBoxメモ,
2626 Undoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2627 this._タブを選択する( タブ種別.基本情報 );
2628 this.numericUpDownメモ用小節番号.Value = (decimal) 任意1;
2629 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2630 this.textBoxメモ.Text = 変更前;
2631 this.textBoxメモ.Focus();
2633 int 小節番号 = (int) ( (decimal) 任意1 );
2635 #region " dicメモ の更新 "
2637 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2639 if( string.IsNullOrEmpty( 変更前 ) )
2640 this.譜面.SSTFormatScore.dicメモ.Remove( 小節番号 );
2642 this.譜面.SSTFormatScore.dicメモ[ 小節番号 ] = 変更前;
2646 if( !string.IsNullOrEmpty( 変更前 ) )
2647 this.譜面.SSTFormatScore.dicメモ.Add( 小節番号, 変更前 );
2652 this._小節の先頭へ移動する( 小節番号 );
2653 this.splitContainer分割パネルコンテナ.Panel2.Refresh(); // 小節メモをリフレッシュ。
2655 Redoアクション: ( 変更対象, 変更前, 変更後, 任意1, 任意2 ) => {
2656 this._タブを選択する( タブ種別.基本情報 );
2657 this.numericUpDownメモ用小節番号.Value = (decimal) 任意1;
2658 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2659 this.textBoxメモ.Text = 変更後;
2660 this.textBoxメモ.Focus();
2662 int 小節番号 = (int) ( (decimal) 任意1 );
2664 #region " dicメモの更新 "
2666 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2668 if( string.IsNullOrEmpty( 変更後 ) )
2669 this.譜面.SSTFormatScore.dicメモ.Remove( 小節番号 );
2671 this.譜面.SSTFormatScore.dicメモ[ 小節番号 ] = 変更後;
2675 if( !string.IsNullOrEmpty( 変更後 ) )
2676 this.譜面.SSTFormatScore.dicメモ.Add( 小節番号, 変更後 );
2681 this._小節の先頭へ移動する( 小節番号 );
2682 this.splitContainer分割パネルコンテナ.Panel2.Refresh(); // 小節メモをリフレッシュ。
2685 変更前の値: this.textBoxメモ_以前の値,
2686 変更後の値: this.textBoxメモ.Text,
2687 任意1: (object) this.numericUpDownメモ用小節番号.Value,
2690 this.UndoRedo管理.セルを追加する( cc );
2693 this.UndoRedo用GUIのEnabledを設定する();
2699 this.textBoxメモ_以前の値 = this.textBoxメモ.Text; // <以前の値> = <現在の値>
2701 if( false == UndoRedo.管理.UndoRedoした直後である )
2704 UndoRedo.管理.UndoRedoした直後である = false;
2706 #region " 小節番号に対応するメモを dicメモ に登録する。"
2709 int 小節番号 = (int) this.numericUpDownメモ用小節番号.Value;
2711 if( string.IsNullOrEmpty( this.textBoxメモ.Text ) )
2714 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2715 this.譜面.SSTFormatScore.dicメモ.Remove( 小節番号 ); // 存在してたら削除。
2721 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2722 this.譜面.SSTFormatScore.dicメモ[ 小節番号 ] = this.textBoxメモ.Text; // 存在してたら更新。
2724 this.譜面.SSTFormatScore.dicメモ.Add( 小節番号, this.textBoxメモ.Text ); // 存在してなかったら追加。
2729 #region " もし最終小節だったなら、後ろに4つ小節を加える。"
2732 int 小節番号 = (int) this.numericUpDownメモ用小節番号.Value;
2733 if( 小節番号 == this.譜面.SSTFormatScore.最大小節番号 )
2735 this.譜面.最後の小節の後ろに小節を4つ追加する();
2741 protected void textBoxメモ_Leave( object sender, EventArgs e )
2743 // 最新 UndoRedoセル の所有権を放棄する。
2744 this.UndoRedo管理.Undoするセルを取得して返す_見るだけ()?.所有権を放棄する( this.textBoxメモ );
2747 this.splitContainer分割パネルコンテナ.Panel2.Refresh();
2749 private string textBoxメモ_以前の値 = "";
2751 protected void numericUpDownメモ用小節番号_ValueChanged( object sender, EventArgs e )
2753 // 小節番号にあわせて、textBoxメモにメモを表示する。
2754 int 小節番号 = (int) this.numericUpDownメモ用小節番号.Value;
2755 this._次のプロパティ変更がUndoRedoリストに載らないようにする();
2756 if( this.譜面.SSTFormatScore.dicメモ.ContainsKey( 小節番号 ) )
2757 this.textBoxメモ.Text = this.譜面.SSTFormatScore.dicメモ[ 小節番号 ];
2759 this.textBoxメモ.Text = "";
2760 this._次のプロパティ変更がUndoRedoリストに載るようにする();