モデルオブジェクトを追加、削除、複製する
モデルオブジェクトを追加、削除、複製する処理はモデルオブジェクト自体で処理できないので、RMGWindowControllerで実装します。取り消しとやり直しに対応させますので、コラム「取り消しとやり直し」を読んでおいて下さい。
アンドゥマネージャーへのアクセスを容易にする
取り消しとやり直しに対応させるためにアンドゥマネージャーへのアクセスが頻繁に必要となります。アクセスを容易にするために、インスタンス変数を用意して初期化の際にセットしておく事にします。
まずインスタンス変数を用意します。
@class OscillatorView,MasterMotif,RepeatingMotif;
@interface RMGWindowController : NSWindowController
{
IBOutlet NSImageView *imageView;
IBOutlet NSArrayController *masterMotifArrayController;
IBOutlet NSArrayController *repeatingMotifArrayController;
IBOutlet OscillatorView *x1View,*x2View,*x3View;
IBOutlet OscillatorView *y1View,*y2View,*y3View;
IBOutlet NSView *lineX,*lineY;
IBOutlet NSView *lineX1,*lineX2,*lineX3;
IBOutlet NSView *lineY1,*lineY2,*lineY3;
IBOutlet NSView *viewX,*viewY,*viewMaster;
IBOutlet NSTabView *mainTabView,*listTabView;
IBOutlet NSSegmentedControl *mainSegmentedControl;
IBOutlet NSSegmentedControl *listSegmentedControl;
IBOutlet NSButton *setMasterMotifButton;
BOOL needsResizeView,needsResizeImageView;
int whiteSpace,viewMargin,lineLength;
OscillatorView *selectedView;
NSArray *masterMotifSelections,*repeatingMotifSelections;
NSUndoManager *undoManager;
MasterMotif *currentMasterMotif;
RepeatingMotif *currentRepeatingMotif;
}
- (NSArray *)masterMotifSelections;
- (NSArray *)repeatingMotifSelections;
- (void)setMasterMotifSelections:(NSArray *)newSelections;
- (void)setRepeatingMotifSelections:(NSArray *)newSelections;
- (void)imageSizeChanged;
- (IBAction)segmentControlClicked:(id)sender;
@end
次に起動時にこの変数をセットします。ドキュメントベースアプリケーションの場合、ドキュメントがアンドゥマネージャーを用意してくれます。
- (void)awakeFromNib
{
[[self document] bind:@"masterMotifSelections"
toObject:masterMotifArrayController
withKeyPath:@"selectedObjects"
options:nil];
[[self document] bind:@"repeatingMotifSelections"
toObject:repeatingMotifArrayController
withKeyPath:@"selectedObjects"
options:nil];
[self bind:@"masterMotifSelections"
toObject:masterMotifArrayController
withKeyPath:@"selectedObjects"
options:nil];
[self bind:@"repeatingMotifSelections"
toObject:repeatingMotifArrayController
withKeyPath:@"selectedObjects"
options:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(oscillatorViewClicked:)
name:RMGOscViewClickedNotification
object:[self window]];
NSSize sizeLineX = NSMakeSize(1,lineLength);
NSSize sizeLineY = NSMakeSize(lineLength,1);
[lineX1 setFrameSize:sizeLineX];
[lineX3 setFrameSize:sizeLineX];
[lineY1 setFrameSize:sizeLineY];
[lineY2 setFrameSize:sizeLineY];
[self windowDidResize:nil];
[self segmentControlClicked:listSegmentedControl];
[self segmentControlClicked:mainSegmentedControl];
undoManager = [[self document] undoManager];
}
MasterMotifオブジェクトを追加、削除、複製する
追加と削除のプリミティブメソッド
取り消しとやり直しに対応させるために、追加と削除のメソッドをペアで用意して互いに呼び合うようにします。詳細はコラム「取り消しとやり直し」を参照の事。
- (void)removeMasterMotif:(id)targetObject
atIndex:(unsigned int)index
{
[[undoManager prepareWithInvocationTarget:self]
insertMasterMotif:targetObject atIndex:index];
[masterMotifArrayController removeObject:targetObject];
}
- (void)insertMasterMotif:(id)targetObject
atIndex:(unsigned int)index
{
[[undoManager prepareWithInvocationTarget:self]
removeMasterMotif:targetObject atIndex:index];
[masterMotifArrayController insertObject:targetObject
atArrangedObjectIndex:index];
}
原図を追加するアクションメソッド
そしてこれとは別にアクションメソッドを用意します。別に用意する理由は二つあります。一つは指定すべきindexの値を決める必要があるからで、もう一つはアクション名を指定する必要があるからです。
新しい原図を追加した場合でも、原図の複製を作った場合でもinsertMasterMotif:atIndex:メソッドを使いますが、取り消しのメニュー項目名はそれぞれに合わせたものであるべきです。
原図を追加するアクションメソッドは以下のようになります。
- (IBAction)addMasterMotif:(id)sender
{
unsigned int index;
MasterMotif *newMotif;
index = [masterMotifArrayController selectionIndex];
if(index == NSNotFound)
index = 0;
else
index++;
newMotif = [[[MasterMotif alloc] init] autorelease];
[newMotif setUndoManager:undoManager];
[self insertMasterMotif:newMotif atIndex:index];
[undoManager setActionName:
NSLocalizedString(@"addMasterMotif",nil)];
}
まずindexを決定します。原図の配列を管理するNSArrayControllerにselectionIndexメッセージを送って、何らかの数値が返ってきたらそれプラス1にします(選択されている原図の次に追加する)。NSNotFoundの場合は配列が空という事なので、indexをゼロにします。
MasterMotifオブジェクトを新しく作り、autoreleaseしておきます。配列に登録した時点でNSArrayオブジェクトがこのオブジェクトを所有するので、ここでは所有権を放棄しておくべきだからです。MasterMotifオブジェクトでもアンドゥマネージャーを使うので、ここでセットしておきます。
insertMasterMotif:atIndex:メソッドを使って、オブジェクトを配列に登録します。そしてアクション名をセットします。アクション名として使う文字列はローカライズされたものを使います。
原図を削除するアクションメソッド
原図を削除するアクションメソッドは以下のようになります。テーブルビューの設定でAllow Empty Selectionにチェックを入れていない事、削除ボタンのenabledプロパティをcanRemoveに設定してある事からindexが不正な値になる事はないので、ノーチェックにしています。
- (IBAction)removeMasterMotif:(id)sender
{
unsigned int index;
index = [masterMotifArrayController selectionIndex];
[self removeMasterMotif:currentMasterMotif atIndex:index];
[undoManager setActionName:
NSLocalizedString(@"removeMasterMotif",nil)];
}
原図の複製を作るアクションメソッド
原図の複製を作るアクションメソッドは以下のようになります。削除と同じ理由により、indexはノーチェックです。現在選択されている原図に対してcopyメッセージを送ってオブジェクトの複製を作り、それを配列に登録します。アクション名は複製アクション用の文字列をセットします。
- (IBAction)duplicateMasterMotif:(id)sender
{
unsigned int index;
MasterMotif *clone;
index = [masterMotifArrayController selectionIndex] + 1;
clone = [[currentMasterMotif copy] autorelease];
[self insertMasterMotif:clone atIndex:index];
[undoManager setActionName:
NSLocalizedString(@"duplicateMasterMotif",nil)];
}
RepeatingMotifオブジェクトを追加、削除、複製する
内容は「MasterMotifオブジェクトを追加、削除、複製する」と同じで変数名が異なるだけですので、説明は省略します。
追加と削除のプリミティブメソッド
- (void)removeRepeatingMotif:(id)targetObject
atIndex:(unsigned int)index
{
[[undoManager prepareWithInvocationTarget:self]
insertRepeatingMotif:targetObject atIndex:index];
[repeatingMotifArrayController removeObject:targetObject];
}
- (void)insertRepeatingMotif:(id)targetObject
atIndex:(unsigned int)index
{
[[undoManager prepareWithInvocationTarget:self]
removeRepeatingMotif:targetObject atIndex:index];
[repeatingMotifArrayController insertObject:targetObject
atArrangedObjectIndex:index];
}
繰返し紋様を追加するアクションメソッド
- (IBAction)addRepeatingMotif:(id)sender
{
unsigned int index;
RepeatingMotif *newMotif;
index = [repeatingMotifArrayController selectionIndex];
if(index == NSNotFound)
index = 0;
else
index++;
newMotif = [[[RepeatingMotif alloc] init] autorelease];
[newMotif setUndoManager:undoManager];
[self insertRepeatingMotif:newMotif atIndex:index];
[undoManager setActionName:
NSLocalizedString(@"addRepeatingMotif",nil)];
}
繰返し紋様を削除するアクションメソッド
- (IBAction)removeRepeatingMotif:(id)sender
{
unsigned int index;
index = [repeatingMotifArrayController selectionIndex];
[self removeRepeatingMotif:currentRepeatingMotif atIndex:index];
[undoManager setActionName:
NSLocalizedString(@"removeRepeatingMotif",nil)];
}
繰返し紋様の複製を作るアクションメソッド
- (IBAction)duplicateRepeatingMotif:(id)sender
{
unsigned int index;
RepeatingMotif *clone;
index = [repeatingMotifArrayController selectionIndex] + 1;
clone = [[currentRepeatingMotif copy] autorelease];
[self insertRepeatingMotif:clone atIndex:index];
[undoManager setActionName:
NSLocalizedString(@"duplicateRepeatingMotif",nil)];
}
インターフェイスファイルの修正
動作上は問題ないのですが、ビルド時に警告が出るので少し修正しておきます。removeMasterMotif:atIndex:とinsertMasterMotif:atIndex:は互いに相手を呼び合うのですが、insertMasterMotif:atIndex:の方がソースファイル上でremoveMasterMotif:atIndex:よりも後で出現する関係上、ビルド時にwarning: no 'insertMasterMotif:atIndex:' method foundと怒られてしまいます。
本来、外部から呼ばれる事のないメソッドなのでインターフェイスファイルに記述しておく必要もないのですが、毎回警告が出るのも気持ち悪いので、インターフェイスファイルに追加しておきます。
@class OscillatorView,MasterMotif,RepeatingMotif;
@interface RMGWindowController : NSWindowController
{
IBOutlet NSImageView *imageView;
IBOutlet NSArrayController *masterMotifArrayController;
IBOutlet NSArrayController *repeatingMotifArrayController;
IBOutlet OscillatorView *x1View,*x2View,*x3View;
IBOutlet OscillatorView *y1View,*y2View,*y3View;
IBOutlet NSView *lineX,*lineY;
IBOutlet NSView *lineX1,*lineX2,*lineX3;
IBOutlet NSView *lineY1,*lineY2,*lineY3;
IBOutlet NSView *viewX,*viewY,*viewMaster;
IBOutlet NSTabView *mainTabView,*listTabView;
IBOutlet NSSegmentedControl *mainSegmentedControl;
IBOutlet NSSegmentedControl *listSegmentedControl;
IBOutlet NSButton *setMasterMotifButton;
BOOL needsResizeView,needsResizeImageView;
int whiteSpace,viewMargin,lineLength;
OscillatorView *selectedView;
NSArray *masterMotifSelections,*repeatingMotifSelections;
NSUndoManager *undoManager;
MasterMotif *currentMasterMotif;
RepeatingMotif *currentRepeatingMotif;
}
- (NSArray *)masterMotifSelections;
- (NSArray *)repeatingMotifSelections;
- (void)setMasterMotifSelections:(NSArray *)newSelections;
- (void)setRepeatingMotifSelections:(NSArray *)newSelections;
- (void)imageSizeChanged;
- (IBAction)segmentControlClicked:(id)sender;
- (void)insertMasterMotif:(id)targetObject
atIndex:(unsigned int)index;
- (void)insertRepeatingMotif:(id)targetObject
atIndex:(unsigned int)index;
@end