今回は、ファイルリストテーブルの Clear ボタンの処理と、ファイルのプレビューをまとめる。
まずは、ファイルリストテーブルで残っている Clear ボタンの扱いから。clearTable というメソッドを作り、ib_action に設定する。
def clearTable(sender)
end
ib_action :clearTable
これで、App Controller の Received Actions に出てくるので、Clear ボタンに結びつける。
Add ボタンと同様に、sender.alternateTitle.to_i で 0 か 1 が得られてボタンを判別できる。これを clearBtn に入れておく。この場合は、メソッド内だけで使えればいいので、ローカル変数にする。
clearBtn = sender.alternateTitle.to_i
Tag を使っている場合は、
clearBtn = sender.tag.to_i
ここで、テーブルのファイルリストを全部消去する場合に、注意が出るようにしてみる。これには、NSAlert の alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat というメソッドを使う。
MessageText - 表示される警告メッセージ
defaultButton - デフォルトで青くなっているボタンのテキスト(返り値 1)
alternateButton - 別の選択肢のボタンのテキスト(返り値 0)
otherButton - もう一つの選択肢のボタンのテキスト(返り値 -1)
informativeText - 追加情報のテキスト
ボタンは3つまで作れて、よくあるのが上から Yes、No、Cancel とか。最初の以外は nil にすることでボタンを表示させなくできる。このサンプルでは、テーブルを善消去するかどうかの確認の警告文と、消去するともとに戻せないという情報文をつけて、ボタンは Yes と No だけにする。
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat("Are you sure you want to clear the table?",
"Yes",
"No",
nil,
"This cannot be undone.")
userChoice = alert.runModal
case userChoice
when 0
return
when 1
end
この case が 1 のところにテーブルを消去する処理を入れる。
これで、次のような警告ウィンドウが現れる。
テーブルのデータを消去するには Array Controller を空にすればいいので、スクリプトから Array Controller にアクセスするに書いた方法をとる。
Array Controller に対して、removeObjects でそのオブジェクトを自身の arrangedObjects にすればいい。このサンプルの場合は、すべての要素がテーブルに表示されているので、setFilterPredicate(nil) を使ってわざわざすべてを表示させなくてもいい。
@fileAryCtl[clearBtn].removeObjects(@fileAryCtl[clearBtn].arrangedObjects)
ただ、配列が長い場合には処理にものすごく時間がかかるので removeObjectsAtArrangedObjectIndexes(indexSet) を使う。
@fileAryCtl[clearBtn].removeObjectsAtArrangedObjectIndexes(NSIndexSet.indexSetWithIndexesInRange([0,@fileAryCtl[clearBtn].arrangedObjects.length]))
これに加えて、もしテーブルにファイルが表示されていない場合は、Clear ボタンをクリックしても何も起きないようにするには、Array Controller の arrangedObjects の長さが 0 の時に return するようにすればいい。
return if @fileAryCtl[clearBtn].arrangedObjects.length == 0
Clear ボタンも Delete ボタンみたいにバインディングで簡単にクリックできなくなる方法があればいいけど、見つからなかったので、Value Transformer を使ってやってみる。細かいことは、独自の Value Transformer を作るにまとめたので、そちらを参照してください。
基本的な作業は ArrayExistTransformer という NSValueTransformer のサブクラスを作り、そこで、返す値のクラスを設定し(ここでは NSNumber)、返す値を設定する transformedValue というメソッドを定義する。ここでは、arrangedObjects を入力値としてとり、その長さが nil か 0 ならば 0 を返し、そうでなければ 1 を返すようにする。
class ArrayExistTransformer < OSX::NSValueTransformer
include OSX
def transformedValueClass
NSNumber.class
end
def allowsReverseTransformation
false
end
def transformedValue(value)
if value.nil? || value.length == 0
return 0
else
return 1
end
end
end
これで、AppController の中で、applicationDidFinishLaunching を定義して、上で作った NSValueTransformer のサブクラスを ArrayExeistTransformer という Value Transformer に設定する。
def applicationDidFinishLaunching(application)
transformer = ArrayExistTransformer.init.alloc
NSValueTransformer.setValueTransformer_forName(transformer,"ArrayExistTransformer")
end
ここで、Interface Builder に戻って、Clear ボタンを選択し Inspector の Bindings で Enabled を開き、Bind to には対応する Array Controller を選び、Model Key Path は空白のままで Value Transformer に ArrayExistTransformer と入力する。これで、テーブルにファイルがないときは Clear ボタンがクリックできなくなる。もうひとつの Clear ボタンも File Array 2 に対して同じ設定をする。
次に、ファイルのプレビューをできるようにする。これには、tableViewSeletionDIdChange(notification) という delegate メソッドを使う。これのために TableView の delegate を App Controller にした。
このメッソドの notification にどのテーブルの選択された列が変わったかの情報も入っている。それを object で取り出す。Ruby で object がどのテーブルかを判断するには、to_s を使って変換しないとうまく行かない。それで、つぎのようなスクリプトになる。
def tableViewSelectionDidChange(notification)
case notification.object.to_s
when @table1.to_s
when @table2.to_s
end
end
まず、@previewCheck というチェックボックスがチェックされていない場合は、プレビューを表示しないようにするので、それの処理を入れる。ついでにそのときは、プレビュー表示を消すようにする。
if @previewCheck.state == 0
@previewText.setString("")
return
end
次に処理に移る。上のスクリプト場合だと、table1 と table2 の時で判断して違う処理をする。ただ、このサンプルの場合は、基本的に同じ処理を別の Array Controller に対してするだけなので、ちょっと工夫してみる。
まず、table1 と table2 にそれぞれ 0 と 1 が割り当てられれば @fileAryCtl が使えるので、@tableID という Hash を awakeFromNib で定義してみる。
def awakeFromNib
@tableID = {@table1.to_s => 0,@table2.to_s => 1}
end
これで、上の tableViewSeletionDIdChange を簡素化できる。ついでに notification.object.to_s を currentTable にしておく。
def tableViewSelectionDidChange(notification)
currentTable = notification.object.to_s
case currentTable
when @table1.to_s,@table2.to_s
end
end
さて、処理になるけど、選択された列のインデックスを得るには selectionIndex を使う。
selectedIndex = @fileAryCtl[@tableID[currentTable]].selectionIndex
これから、選択された列のファイルパスと文字コードのインデックスを得る。Array Controller の arrangedObjects は NSDictionary の配列で返ってくるので、それを Ruby の Hash として扱う。
filePath = @fileAryCtl[@tableID[currentTable]].arrangedObjects[selectedIndex]["fullPath"]
encode = @fileAryCtl[@tableID[currentTable]].arrangedObjects[selectedIndex]["encoding"]
ここでの文字コードのインデックスは、Pop Up Button のアイテムのインデックスなので、Cocoa が理解するものと違う。これを変換するために、次のような配列を作る。これは定数にしておく。長いので、2行に分割してある。ちなみに、ここでの Shift-JIS は、Mac の Shift-JIS ではなく、Windows のものである。
EncodingArray = [NSUTF8StringEncoding,NSWindowsCP1252StringEncoding,NSWindowsCP1250StringEncoding,NSASCIIStringEncoding,
NSShiftJISStringEncoding,NSJapaneseEUCStringEncoding,NSISO2022JPStringEncoding]
これらの情報を使って、ファイルを読み込んでテキストを表示させる。
まず、File.extname(filePath.to_s).downcase で拡張子を判別して、.txt、.pdf、それ以外で処理を分ける。
.txt - NSString.alloc.initWithContentsOfFile_encoding_error でパスと文字コードを指定して読み込む。
.pdf - PDFDocument.alloc.initWithURL で読み込んで string で文字情報を取り出す。
その他 - NSAttributedString.alloc.initWithPath_documentAttributes で AttributedString として文字情報を取り出し string で NSString に変換する。
これで取り出したテキストを @previewTable に setString で表示させる。そのとき、scrollRangeToVisible([0,0]) でテキストの先頭が表示されるようにする。
これをスクリプトにすると次のような感じになる。
case File.extname(filePath.to_s).downcase
when ".doc", ".docx", ".rtf", ".rtfd", ".odt", ".sxw"
text = NSAttributedString.alloc.initWithPath_documentAttributes(filePath,nil).string
when ".pdf"
text = PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(filePath)).string unless PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(filePath)) == nil
when ".txt"
text = NSString.alloc.initWithContentsOfFile_encoding_error(filePath,EncodingArray[encode].to_i,nil)
end
@previewText.setString(text)
@previewText.scrollRangeToVisible([0,0])
このファイルを読み込んで NSString にするところは、単語リストを作るときにも使うので、使い回しが聞くようにメソッドとして定義しておく。
def readTextFromFile(filePath,encode)
case File.extname(filePath.to_s).downcase
when ".doc", ".docx", ".rtf", ".rtfd", ".odt", ".sxw"
return NSAttributedString.alloc.initWithPath_documentAttributes(filePath,nil).string
when ".pdf"
return PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(filePath)).string unless PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(filePath)) == nil
when ".txt"
return NSString.alloc.initWithContentsOfFile_encoding_error(filePath,EncodingArray[encode].to_i,nil)
end
end
これをまとめると、こんな感じになる。
def tableViewSelectionDidChange(notification)
if @previewCheck.state == 0
@previewText.setString("")
return
end
currentTable = notification.object.to_s
case currentTable
when @table1.to_s,@table2.to_s
selectedIndex = @fileAryCtl[@tableID[currentTable]].selectionIndex
filePath = @fileAryCtl[@tableID[currentTable]].arrangedObjects[selectedIndex]["fullPath"]
encode = @fileAryCtl[@tableID[currentTable]].arrangedObjects[selectedIndex]["encoding"]
@previewText.setString(readTextFromFile(filePath,encode))
@previewText.scrollRangeToVisible([0,0])
end
end
ちょっと長くなったので、ドラッグアンドドロップはまた次回。
単語リストプログラム その9 - ファイルリストテーブルへのファイルのドラッグアンドドロップ