ホームページ開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでリストボックスのセル種別を増やしてみる(改訂版)

 Xojo / Real Studio Trial and Error

CocoaのDeclareでリストボックスのセル種別を増やしてみる(改訂版)

目次
 はじめに

 以下は、Xojo Cocoaビルドについての話題です。

 Xojo標準のリストボックスのセル種別は、テキストとチェックボックスだけですが、これを増やすことができないか試してみました。
 なお検証には、Xojo 2016 Release 3を用いています。(Mac mini mid 2010 + OS X 10.13.6 High Sierra)
注)初版は、ファイルから読み込んだ行がリストボックスの高さを超える(即ち、縦スクロールが発生する)と、(スクロールして)隠れていたセルを表示しても、値が未セットになってしまう問題(以下、スクロール問題)が確認されたため、対応した改訂版としました。(旧版はこちら

 方針

 当初は、XojoのListBoxクラスを拡張すればいいか、と軽く考えていたのですが、調べてみるとXojoのListBox(XOJListboxView)はNSTableViewではなく、NSViewのサブクラスであることが分かりました。
 つまり、クラス拡張の手は使えませんので、(仕方なく)NSTableViewのサブクラスを独自に実装することしました。

 さて、NSTableViewにはCell BaseとView Baseの二つの方法があるとのことで、現在はView Baseが推奨されているようです。

 参考サイト(1):NSCellを使ってはいけない - Qiita
 参考サイト(2):NSTableView - src256 wiki

 なので、View Baseでいくことにします。(注:NSTableViewがView Baseに対応したのは10.7 Lionとのことなので、動作環境はそれ以降となります。)
 ただ、Cell Baseが非推奨になったとはいえ、ネット上にはCell Baseの情報の方が多いので、参照する場合には注意が必要です。(当初は違いがよく判らず、混乱しました。)

 NSTableViewでのデータハンドリングの特徴(の一つ)は、セルとは別にKey-Value Codingに対応したデータ(以下、内部データ)を持つ点ではなかろうかと思われます。
 Cell Baseでは、tableView:dataCellForTableColumn:row:でオブジェクトをセットしておき、セルを移動したりするとtableView:objectValueForTableColumn:row:が呼ばれるのですが、ここで内部データを返す必要があるため、セルを編集した場合はこのメソッドが呼ばれるまでの間に、内部データに書き戻す必要が生じます。

 一方View Baseでは、tableView:viewForTableColumn:row:でオブジェクトと値をセットしますが、セル移動時には(確認できた範囲内では)何も呼ばれません。
 そのため、内部データを省略することもできてしまうので、当初は内部データを作らない方式を試したのですが、スクロール問題が発生したため、(正攻法である?)内部データを保持する方式とします。(ただし、Key-Value Codingは意識していません。)

 次にViewの中身ですが、チェックボックスを含むNSButtonは、alloc/initしてボタンタイプを指定するやり方は既にDeprecatedになっていて、別途メソッドが用意されていますが、ここでは(10.14までは有効なこともあり)他と合わせる意味もあって、alloc/init方式としました。
 なお、新方式を利用する場合は、リセット時のreleaseは行わないよう、対策する必要があります。(allocしていないので、releaseすると異常終了する。)

 また、ネット上のサンプルは、多くがインターフェイスビルダーを使う前提で書かれているのですが、Xojoでは使えませんので、全てコードで書くにあたって、以下を参考にさせて頂きました。(注:Cell Baseなので、そのままの適用は不可。)

 参考サイト(3):NSTableView (Creation and Loading from Code) – Knowledge Stack

 以上を踏まえ、(残りの)仕様は以下の通りとしました。

 Xojoでの実装
【ソースコードのコピー&ペーストについて】
ソースコード(グレー背景部分の全文)をコピーし、指定のウィンドウ/クラスにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
ただし、この方法は、メソッドでは問題ないようですが、イベント/アクション/プロパティでは不安定?なので、ペーストできない場合は、各項目のカッコ内を適用して下さい。
  1. Xojoで新規プロジェクトを作成
  2. Toolbarをプロジェクトに追加(Name:Toolbar1)後、以下のアイテムを追加。その後、Window1に追加(Name:Toolbar11)。
    ToolItem1(開く)/ToolItem2(保存)/ToolItem3(スペース)/ToolItem4(一行追加)/ToolItem5(一列追加)
  3. 以下をToolbar11にペースト(できなければ、Sub - Endの間をToolbar11のActionイベントに記述)
    Sub Action(item As ToolItem) Handles Action
      select case Item.Name
      case "ToolItem1"
        LoadFile()
        
      case "ToolItem2"
        SaveFile()
        
      case "ToolItem4"
        Dim colCnt As Integer = GetColCount()
        Dim rowData(-1) As String
        ReDim rowData(colCnt)  // 列数分の空の行データ
        
        AddRow(rowData)
        
      case "ToolItem5"
        AddCol("col "+Str(GetColMaxVal()),150,"text")
        
      end select
    End Sub
    
  4. 以下をWindow1にペースト(できなければ、Sub - Endの間をOpenイベントに記述)
    Sub Open() Handles Open
      // NSTableView。引数は順に、生成したインスタンス、配置するウィンドウ、位置/サイズ、Delegateメソッド
      Dim d As NSViewListBox = new NSViewListBox(tableviewInst, self, NSMakeRect(16, 16, me.Width-32, me.Height-32), _
      AddressOf actionEvent, AddressOf viewForTable, AddressOf sortDescriptors, AddressOf didClickTableColumn, AddressOf textEndEdit)
      
      // 内部データの初期化
      InitDataArray()
    End Sub
    
  5. 以下をWindow1にペースト
    Protected Sub actionEvent(id As Ptr, sel as CString, sender As Ptr)
      // 変更後の値を内部データに反映させる
      actionEventValue(id,sender)
    End Sub
    
  6. 以下をWindow1にペースト
    Protected Sub actionEventValue(tableView As Ptr, cellView As Ptr)
      // 値を変更したセルの行位置を取得
      Declare Function rowForView Lib "Cocoa" Selector "rowForView:" (receiver As Ptr, view As Ptr) As Integer
      Dim row As Integer = rowForView(tableView, cellView)
      
      // 値を変更したセルの列位置を取得
      Declare Function columnForView Lib "Cocoa" Selector "columnForView:" (receiver As Ptr, view As Ptr) As Integer
      Dim col As Integer = columnForView(tableView, cellView)
      
      // 全ての列を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableView)
      if colAry=nil then  // 取得できなければ戻る
        return
      end if
      
      // 列クラスを取得
      Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, w As Integer) As Ptr
      Dim tCol As Ptr = objectAtIndex(colAry, col)
      
      // 列のアイデンティファイアを取得
      Declare Function identifier Lib "Cocoa" Selector "identifier" (receiver As Ptr) As CFStringRef
      Dim ident As String = identifier(tCol)
      
      // アイデンティファイアから列種別と固有化番号を取得
      Dim kind As String = NthField(ident,":",1)
      Dim colNo As Integer = Val(NthField(ident,":",2))
      
      Dim st As String
      select case kind
      case "text"  // 文字列
        // 文字列を取得
        Declare Function stringValue Lib "Cocoa" Selector "stringValue" (receiver As Ptr) As CFStringRef
        st=stringValue(cellView)
        
      case "chkb"  // チェックボックス
        // Stateを取得
        Declare Function state Lib "Cocoa" Selector "state" (receiver As Ptr) As Integer
        st=Str(state(cellView))
        
      case "popu"  // ポップアップメニュー
        // ListIndexを取得
        Declare Function indexOfSelectedItem Lib "Cocoa" Selector "indexOfSelectedItem" (receiver As Ptr) As Integer
        st=Str(indexOfSelectedItem(cellView))
        
      case "segm"  // セグメンテッドコントロール
        // Segmentを取得
        Declare Function selectedSegment Lib "Cocoa" Selector "selectedSegment" (receiver As Ptr) As Integer
        st=Str(selectedSegment(cellView))
        
      case "slid"  // スライダー
        // スライダー値を取得
        Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double
        st=Str(doubleValue(cellView))
        
      case "indi"  // レベルインジケーター
        // インジケーター値を取得
        Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double
        st=Str(doubleValue(cellView))
        
      end select
      
      // 行データを取得
      'Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
      Dim objct As Ptr = objectAtIndex(dataArrayNS, row)
      
      // 列アイデンティファイアをキーにして、Dictionaryの値を変更
      Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As CFStringRef, key As CFStringRef)
      setObject(objct, st, ident)
    End Sub
    
  7. 以下をWindow1にペースト
    Protected Sub AddCol(title As String, width As CGFloat, kind As String)
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr
      
      // 列クラス(NSTableColumn)のインスタンスを生成
      Dim ident As String = kind+":"+Str(GetColMaxVal())
      Dim tCol As Ptr = NSClassFromString("NSTableColumn")  // クラスメソッドなので、まずNSTableColumnクラスを取得
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function initWithIdentifier Lib "Cocoa" selector "initWithIdentifier:" (obj_id As Ptr, ifer As CFStringRef) As Ptr
      tCol = initWithIdentifier(alloc(tCol), ident)
      
      // 内部データ生成
      AddColDictionary(ident)
      
      // 列幅をセット
      Declare Sub setWidth Lib "Cocoa" Selector "setWidth:" (receiver As Ptr, w As CGFloat)
      setWidth(tCol, width)
      
      // タイトルをセット
      Declare Sub setTitle Lib "Cocoa" Selector "setTitle:" (receiver As Ptr, name As CFStringRef)
      setTitle(tCol, title)
      
      // ヘッダプレスによるソートを許可
      Dim pnt1 As Ptr = NSClassFromString("NSSortDescriptor")  // クラスメソッドなので、まずNSSortDescriptorクラスを取得
      Declare Function sortDescriptor Lib "Cocoa" Selector "sortDescriptorWithKey:ascending:selector:" (receiver As Ptr, obj1 As CFStringRef, obj2 As Boolean, obj3 As Ptr) As Ptr
      pnt1 = sortDescriptor(pnt1, ident, true, NSSelectorFromString("compare:"))
      Declare Sub setSortDescriptors Lib "Cocoa" Selector "setSortDescriptorPrototype:" (receiver As Ptr, typ As Ptr)
      setSortDescriptors(tCol, pnt1)
      
      // 列クラスのインスタンスをテーブルビューに追加
      Declare Sub addTableColumn Lib "Cocoa" Selector "addTableColumn:" (receiver As Ptr, col As Ptr)
      addTableColumn(tableViewInst, tCol)
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(tCol)
    End Sub
    
  8. 以下をWindow1にペースト
    Protected Sub AddColDictionary(ident As String)
      // 行のループ
      for row As Integer = 0 to rowMax-1
        
        // 行データDictionaryの取得
        Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
        Dim rowDict As Ptr = objectAtIndex(dataArrayNS, row)
        
        // 列アイデンティファイアをキーにして、行データDictionaryに追加
        Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As CFStringRef, key As CFStringRef)
        setObject(rowDict, "", ident)  // データは空
        
      next
    End Sub
    
  9. 以下をWindow1にペースト
    Protected Sub AddRow(rowData() As String)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 内部データ生成
      AddRowDictionary(rowData)
      
      // NSIndexSet生成
      Dim indexes As Ptr = NSClassFromString("NSMutableIndexSet")  // クラスメソッドなので、まずNSMutableIndexSetクラスを取得
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr
      indexes = init(alloc(indexes))
      Declare Sub addObject Lib "Cocoa" Selector "addIndex:" (receiver As Ptr, val As Integer)
      addObject(indexes, rowMax)
      
      // 行を追加
      Declare Sub insertRows Lib "Cocoa" Selector "insertRowsAtIndexes:withAnimation:" (receiver As Ptr, idxset As Ptr, anime As Integer)
      insertRows(tableViewInst, indexes, 0)
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(indexes)
      
      // 行数をカウントアップ
      rowMax=rowMax+1
    End Sub
    
  10. 以下をWindow1にペースト
    Protected Sub AddRowDictionary(dataArray() As String)
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      Dim dict1 As Ptr = NSClassFromString("NSMutableDictionary")
      Declare Function dictionary Lib "Cocoa" Selector "dictionary" (receiver As Ptr) As Ptr
      dict1 = dictionary(dict1)
      
      // 列集合を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableviewInst)
      
      // 列数を取得
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim colCnt As Integer = count(colAry)
      
      Dim col As Integer
      Dim st As String
      for col=0 to colCnt-1  // 列のループ
        
        // 列を順に取得
        Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
        Dim column As Ptr = objectAtIndex(colAry, col)
        
        // 列アイデンティファイア
        Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef
        Dim ident As String = identifier(column)
        
        // 値をセット
        st=dataArray(col)
        
        // 列アイデンティファイアをキーにして、Dictionaryに追加
        Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As CFStringRef, key As CFStringRef)
        setObject(dict1, st, ident)
        
      next
      
      // 行配列に追加
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
      addObject(dataArrayNS, dict1)
    End Sub
    
  11. 以下をWindow1にペースト
    Protected Sub didClickTableColumn(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr)
      // SortDescriptor生成
      Dim dsc As Ptr = GetSortDesc(tableColumn)
      
      // ソート
      declare sub sortUsingDescriptors lib "Cocoa" Selector "sortUsingDescriptors:" (obj as ptr, desc as Ptr)
      sortUsingDescriptors(dataArrayNS, dsc)
      
      // ソート後の内部データをセルに書き戻す
      SetSortedData(dataArrayNS)
    End Sub
    
  12. 以下をWindow1にペースト
    Protected Function GetClassName(cls As Ptr) as String
      // クラスを取得
      Declare Function myClass Lib "Cocoa" selector "class" (class_id As Ptr) As Ptr
      Dim pnt1 As Ptr = myClass(cls)
      
      // クラス名を取得
      Declare Function NSStringFromClass Lib "Cocoa" (aClass As Ptr) As CFStringRef
      Dim cnam As String = NSStringFromClass(pnt1)
      
      // クラス名を返す
      return cnam
    End Function
    
  13. 以下をWindow1にペースト
    Protected Function GetColCount() as Integer
      // 列集合を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableviewInst)
      
      // 列数を取得
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim colCnt As Integer = count(colAry)
      
      // 列数を返す
      return colCnt
    End Function
    
  14. 以下をWindow1にペースト
    Protected Function GetColMaxVal() as Integer
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // データ領域の生成
      Dim dataArray As Ptr
      dataArray = NSClassFromString("NSMutableArray")  // クラスメソッドなので、まずNSMutableArrayクラスを取得
      Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr  // Return Array*
      dataArray = getArray(dataArray)
      
      // 列集合を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableviewInst)
      
      // 列数を取得
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim colCnt As Integer = count(colAry)
      
      // 列のループ
      Dim col, colmax As Integer
      for col=0 to colCnt-1
        
        // 列を順に取得
        Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
        Dim column As Ptr = objectAtIndex(colAry, col)
        
        // 列アイデンティファイア
        Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef
        Dim ident As String = identifier(column)
        
        // 固有化番号を取得
        Dim colNo As Integer = Val(NthField(ident,":",2))
        
        // 固有化番号の最大値を保持
        if colNo>colMax then
          colMax=colNo
        end if
        
      next
      
      // 固有化番号の最大値の次の値を返す
      return colMax+1
    End Function
    
  15. 以下をWindow1にペースト
    Protected Function GetSortDesc(tableColumn As Ptr) as Ptr
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr
      
      // 列アイデンティファイアの取得
      Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef
      Dim ident As String = identifier(tableColumn)
      
      // ソート順(昇順/降順)のセット
      Dim ascend As Boolean = true
      if oldDescs<>nil then  // 初回はoldDescs=nilなので除く(trueが渡る)
        
        // oldDescsの要素分ループ
        Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
        Dim cnt As Integer = count(oldDescs)
        for i As Integer = 0 to cnt-1
          
          // 要素を順に取得
          Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, w As Integer) As Ptr
          Dim odesc As Ptr = objectAtIndex(oldDescs, i)
          
          // キー(アイデンティファイア)を取得
          Declare Function myKey Lib "Cocoa" Selector "key" (receiver As Ptr) As CFStringRef
          Dim ident2 As String = myKey(odesc)
          
          // 前回のデスクリプターが見つかったら
          if ident=ident2 then
            Declare Function ascending Lib "Cocoa" Selector "ascending" (receiver As Ptr) As Boolean
            if ident<>previousCol then  // 異なる列を選択した場合、ソートマークは変化しないので、前回の値をそのまま使用
              ascend = ascending(odesc)
            else  // 同じ列を選択した場合、ソートマークが変化するので、前回の値を反転して使用
              ascend = not ascending(odesc)
            end if
            exit
          end if
          
        next
        
      end if
      
      // SortDescriptorを生成
      Dim pnt1 As Ptr = NSClassFromString("NSSortDescriptor")  // クラスメソッドなので、まずNSSortDescriptorクラスを取得
      Declare Function sortDescriptor Lib "Cocoa" Selector "sortDescriptorWithKey:ascending:selector:" (receiver As Ptr, key As CFStringRef, asc As Boolean, sel As Ptr) As Ptr
      pnt1 = sortDescriptor(pnt1, ident, ascend, NSSelectorFromString("compare:"))
      
      // 配列を生成
      Dim arr As Ptr = NSClassFromString("NSMutableArray")  // クラスメソッドなので、まずNSMutableArrayクラスを取得
      Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr  // Return Array*
      arr = getArray(arr)
      
      // SortDescriptorを追加
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
      addObject(arr, pnt1)
      
      // ヘッダをプレスした列のアイデンティファイアを保持
      previousCol=ident
      
      // リストを返す
      return arr
    End Function
    
  16. 以下をWindow1にペースト
    Protected Sub InitDataArray()
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 参照数の取得
      Declare Function retainCount Lib "Cocoa" selector "retainCount" (class_id As Ptr) As Integer
      Dim cnt As Integer = retainCount(dataArrayNS)
      
      // 参照があったら、解放する
      if cnt>0 then
        // clean up
        Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
        release(dataArrayNS)
      end if
      
      // データ領域の生成
      dataArrayNS = NSClassFromString("NSMutableArray")  // クラスメソッドなので、まずNSMutableArrayクラスを取得
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr
      dataArrayNS = init(alloc(dataArrayNS))
    End Sub
    
  17. 以下をWindow1にペースト
    Protected Sub LoadFile()
      Dim f As FolderItem
      Dim tis As TextInputStream
      Dim line As String
      
      // ファイルオープンダイアログ
      f=GetOpenFolderItem("text/plain") // defined as a FileType
      if f=nil or not f.Exists then
        return
      end if
      
      tis=TextInputStream.Open(f)  // ファイルを開く
      if tis=nil then
        return
      end if
      tis.Encoding=Encodings.UTF8  // テキストエンコーディングはUTF-8
      
      // まず、初期化
      ResetTable()
      
      line=tis.ReadLine  // 一ライン読み込み
      Dim colTitle(-1) As String
      colTitle=Split(line, ",")  // 列タイトル
      
      line=tis.ReadLine  // 一ライン読み込み
      Dim colWidth(-1) As String
      colWidth=Split(line, ",")  // 列幅
      
      line=tis.ReadLine  // 一ライン読み込み
      Dim colKind(-1) As String
      colKind=Split(line, ",")  // 列種別
      
      // 列生成
      for i As Integer = 0 to colTitle.Ubound
        AddCol(colTitle(i),Val(colWidth(i)),colKind(i))
      next
      
      // ラインの終りまで繰り返す
      rowMax=0
      while not tis.EOF
        
        line=tis.ReadLine  // 一ライン読み込み
        Dim rowData(-1) As String
        rowData=Split(line, ",")  // 一行分のセルデータ配列
        AddRow(rowData)  // 一行追加
        
      wend
      
      // ストリームのクローズ
      tis.Close  
    End Sub
    
  18. 以下をWindow1にペースト
    Protected Function makeMenuArray() as Ptr
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      Dim arr As Ptr = NSClassFromString("NSMutableArray")  // クラスメソッドなので、まずNSMutableArrayクラスを取得
      Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr  // Return Array*
      arr=getArray(arr)
      
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As CFStringRef)
      addObject(arr, "popup 0")
      addObject(arr, "popup 1")
      addObject(arr, "popup 2")
      addObject(arr, "popup 3")
      addObject(arr, "popup 4")
      addObject(arr, "popup 5")
      addObject(arr, "popup 6")
      addObject(arr, "popup 7")
      addObject(arr, "popup 8")
      addObject(arr, "popup 9")
      addObject(arr, "popup 10")
      
      // メニューリストを返す
      return arr
    End Function
    
  19. 以下をWindow1にペースト
    Protected Sub RemoveAllCol()
      // 全ての列を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableviewInst)
      
      // 取得できなければ戻る
      if colAry=nil then
        return
      end if
      
      // 列数を取得
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim colCnt As Integer = count(colAry)
      
      // 列がなければ戻る
      if colCnt<=0 then
        return
      end if
      
      // 列を削除(最後から消していく)
      for i As Integer = colCnt-1 downto 0
        
        Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, w As Integer) As Ptr
        Dim tCol As Ptr = objectAtIndex(colAry, i)
        
        Declare Sub removeTableColumn Lib "Cocoa" Selector "removeTableColumn:" (receiver As Ptr, col As Ptr)
        removeTableColumn(tableViewInst, tCol)
        
      next
    End Sub
    
  20. 以下をWindow1にペースト
    Protected Sub RemoveAllRow()
      // 行がなければ戻る
      if rowMax<=0 then
        return
      end if
      
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // NSIndexSet生成(全行を含む)
      Dim indexes As Ptr = NSClassFromString("NSMutableIndexSet")  // クラスメソッドなので、まずNSMutableIndexSetクラスを取得
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr
      indexes = init(alloc(indexes))
      Declare Sub addObject Lib "Cocoa" Selector "addIndex:" (receiver As Ptr, val As Integer)
      for i As Integer = 0 to rowMax-1
        addObject(indexes, i)
      next
      
      // 行を削除
      Declare Sub removeRows Lib "Cocoa" Selector "removeRowsAtIndexes:withAnimation:" (receiver As Ptr, idxset As Ptr, anime As Integer)
      removeRows(tableViewInst, indexes, 0)
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(indexes)
    End Sub
    
  21. 以下をWindow1にペースト
    Protected Sub RemoveCellView()
      // 列数を取得
      Dim colCnt As Integer = GetColCount()
      
      // Viewのリリース
      Dim col, row As Integer
      for row=0 to rowMax-1  // 行のループ
        for col=0 to colCnt-1  // 列のループ
          
          // 列番号と行番号を指定して、Viewを取得
          Declare Function viewAtColumn Lib "Cocoa" Selector "viewAtColumn:row:makeIfNecessary:" (receiver As Ptr, col As Integer, row As Integer, flg As Boolean) As Ptr
          Dim cellView As Ptr = viewAtColumn(tableviewInst, col, row, true)
          
          // clean up
          Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
          release(cellView)
          
        next
      next
    End Sub
    
  22. 以下をWindow1にペースト
    Protected Sub ResetTable()
      // セルビューのリリース
      RemoveCellView()
      
      // 全行削除
      RemoveAllRow()
      
      // 行カウンターの初期化
      rowMax=0
      
      // 全列削除
      RemoveAllCol()
      
      // 内部データの初期化
      InitDataArray()
    End Sub
    
  23. 以下をWindow1にペースト
    Protected Sub SaveFile()
      Dim f As FolderItem
      Dim tos As TextOutputStream
      Dim col, row As Integer
      Dim st, sep, cnam As String
      
      // ファイル保存ダイアログ
      f=GetSaveFolderItem("","MyInfo.txt")
      if f=nil then
        return
      end if
      
      tos=TextOutputStream.Create(f)  // ファイルを生成
      
      st=""  // ラインの初期化
      for col=0 to GetColCount()-1  // 列のループ
        // 連結の開始時はカンマを追加しない
        sep=","
        if col=0 then sep=""
        st=st+sep+SaveFile2(col,"t").StringValue  // ラインに連結
      next
      tos.WriteLine(st)  // ラインの書き出し
      
      st=""  // ラインの初期化
      for col=0 to GetColCount()-1  // 列のループ
        // 連結の開始時はカンマを追加しない
        sep=","
        if col=0 then sep=""
        Dim w As CGFloat = SaveFile2(col,"w")  // ラインに連結
        st=st+sep+Str(w)
      next
      tos.WriteLine(st)  // ラインの書き出し
      
      st=""  // ラインの初期化
      for col=0 to GetColCount()-1  // 列のループ
        // 連結の開始時はカンマを追加しない
        sep=","
        if col=0 then sep=""
        st=st+sep+SaveFile2(col,"k").StringValue  // ラインに連結
      next
      tos.WriteLine(st)  // ラインの書き出し
      
      for row=0 to rowMax-1  // 行のループ
        st=""  // ラインの初期化
        for col=0 to GetColCount()-1  // 列のループ
          
          // 連結の開始時はカンマを追加しない
          sep=","
          if col=0 then sep=""
          
          // 列番号と行番号を指定して、Viewを取得
          Declare Function viewAtColumn Lib "Cocoa" Selector "viewAtColumn:row:makeIfNecessary:" (receiver As Ptr, col As Integer, row As Integer, flg As Boolean) As Ptr
          Dim cellView As Ptr = viewAtColumn(tableviewInst, col, row, true)
          
          // クラス名を取得
          cnam = GetClassName(cellView)
          
          select case cnam
          case "NSTextField"  // 文字列
            // 文字列を取得
            Declare Function stringValue Lib "Cocoa" Selector "stringValue" (receiver As Ptr) As CFStringRef
            st=st+sep+stringValue(cellView)  // ラインに連結
            
          case "NSButton"  // チェックボックス
            // Stateを取得
            Declare Function state Lib "Cocoa" Selector "state" (receiver As Ptr) As Integer
            st=st+sep+Str(state(cellView))  // ラインに連結
            
          case "NSPopUpButton"  // ポップアップメニュー
            // ListIndexを取得
            Declare Function indexOfSelectedItem Lib "Cocoa" Selector "indexOfSelectedItem" (receiver As Ptr) As Integer
            st=st+sep+Str(indexOfSelectedItem(cellView))  // ラインに連結
            
          case "NSSegmentedControl"  // セグメンテッドコントロール
            // Segmentを取得
            Declare Function selectedSegment Lib "Cocoa" Selector "selectedSegment" (receiver As Ptr) As Integer
            st=st+sep+Str(selectedSegment(cellView))  // ラインに連結
            
          case "NSSlider"  // スライダー
            // スライダー値を取得
            Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double
            st=st+sep+Str(doubleValue(cellView))  // ラインに連結
            
          case "NSLevelIndicator"  // レベルインジケーター
            // インジケーター値を取得
            Declare Function doubleValue Lib "Cocoa" Selector "doubleValue" (receiver As Ptr) As Double
            st=st+sep+Str(doubleValue(cellView))  // ラインに連結
            
          end select
          
        next
        tos.WriteLine(st)  // ラインの書き出し
      next
      
      // ストリームのクローズ
      tos.Close
    End Sub
    
  24. 以下をWindow1にペースト
    Protected Function SaveFile2(idx As Integer, param As String) as Variant
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // データ領域の生成
      Dim dataArray As Ptr
      dataArray = NSClassFromString("NSMutableArray")  // クラスメソッドなので、まずNSMutableArrayクラスを取得
      Declare Function getArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr  // Return Array*
      dataArray = getArray(dataArray)
      
      // 列集合を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableviewInst)
      
      // 指定された列を取得
      Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
      Dim column As Ptr = objectAtIndex(colAry, idx)
      
      // 指定された種別のパラメータを返す
      select case param
      case "t"
        Declare Function title Lib "Cocoa" Selector "title" (receiver As Ptr) As CFStringRef
        Dim s As String = title(column)  // タイトルを取得
        return s
        
      case "w"
        Declare Function width Lib "Cocoa" Selector "width" (receiver As Ptr) As CGFloat
        Dim w As CGFloat = width(column)  // 列幅を取得
        return w
        
      case "k"
        Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef
        Dim ident As String = identifier(column)  // 列アイデンティファイア
        Dim s As String = NthField(ident,":",1)  // 列種別を取得
        return s
        
      end select
    End Function
    
  25. 以下をWindow1にペースト
    Protected Sub SetSortedData(dataArray As Ptr)
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 列集合を取得
      Declare Function tableColumns Lib "Cocoa" Selector "tableColumns" (receiver As Ptr) As Ptr
      Dim colAry As Ptr = tableColumns(tableviewInst)
      
      // 列数を取得
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim colCnt As Integer = count(colAry)
      
      // Viewのループ
      Dim col, row As Integer
      Dim st, cnam As String
      for row=0 to rowMax-1  // 行のループ
        for col=0 to colCnt-1  // 列のループ
          
          // 列を順に取得
          Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
          Dim column As Ptr = objectAtIndex(colAry, col)
          
          // 列アイデンティファイア
          Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef
          Dim ident As String = identifier(column)
          
          // 順に行データを取得
          Dim objct As Ptr = objectAtIndex(dataArray, row)
          
          // 列アイデンティファイアが一致するデータを取得
          Declare Function objectForKey Lib "Cocoa" Selector "objectForKey:" (receiver As Ptr, key As CFStringRef) As CFStringRef
          st = objectForKey(objct, ident)
          
          // 列番号と行番号を指定して、Viewを取得
          Declare Function viewAtColumn Lib "Cocoa" Selector "viewAtColumn:row:makeIfNecessary:" (receiver As Ptr, col As Integer, row As Integer, flg As Boolean) As Ptr
          Dim cellView As Ptr = viewAtColumn(tableviewInst, col, row, true)
          
          // クラス名を取得
          cnam=GetClassName(cellView)
          
          // 値をセット
          select case cnam
          case "NSTextField"  // 文字列
            Declare Sub setStringValue Lib "Cocoa" Selector "setStringValue:" (receiver As Ptr, val As CFStringRef)
            setStringValue(cellView, st)
            
          case "NSButton"  // チェックボックス
            Declare Sub setState Lib "Cocoa" Selector "setState:" (receiver As Ptr, val As Integer)
            setState(cellView, Val(st))
            
          case "NSPopUpButton"  // ポップアップメニュー
            Declare Sub selectItemAtIndex Lib "Cocoa" Selector "selectItemAtIndex:" (receiver As Ptr, val As Integer)
            selectItemAtIndex(cellView, Val(st))
            
          case "NSSegmentedControl"  // セグメンテッドコントロール
            Declare Sub setSelectedSegment Lib "Cocoa" Selector "setSelectedSegment:" (receiver As Ptr, val As Integer)
            setSelectedSegment(cellView, Val(st))
            
          case "NSSlider"  // スライダー
            Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double)
            setDoubleValue(cellView, Val(st))
            
          case "NSLevelIndicator"  // レベルインジケーター
            Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double)
            setDoubleValue(cellView, Val(st))
            
          end select
          
        next
      next
    End Sub
    
  26. 以下をWindow1にペースト
    Protected Sub sortDescriptors(id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr)
      // 過去のデスクリプタを保持
      Declare Function mutableCopy Lib "Cocoa" Selector "mutableCopy" (receiver As Ptr) As Ptr
      oldDescs = mutableCopy(oldDescriptors)
    End Sub
    
    注)当初は単純に代入していたが、oldDescriptorsはここを過ぎると解放されてしまうようで、参照が不安定になるため、コピーするようにした。(2021.04.20)
  27. 以下をWindow1にペースト
    Protected Function textEndEdit(id as Ptr, sel as CString, control As Ptr, fEditor As Ptr) as Boolean
      // 編集後の値を内部データに反映させる
      actionEventValue(id,control)
      
      return true
    End Function
    
  28. 以下をWindow1にペースト
    Protected Function viewForTable(id As Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer) as Ptr
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr
      
      // 列のアイデンティファイアを取得
      Declare Function identifier Lib "Cocoa" Selector "identifier" (receiver As Ptr) As CFStringRef
      Dim ident As String = identifier(tableColumn)
      
      // アイデンティファイアから列種別と固有化番号を取得
      Dim kind As String = NthField(ident,":",1)
      
      // 行データを取得
      Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, idx As Integer) As Ptr
      Dim objct As Ptr = objectAtIndex(dataArrayNS, rowidx)
      
      // 列アイデンティファイアが一致するデータを取得
      Declare Function objectForKey Lib "Cocoa" Selector "objectForKey:" (receiver As Ptr, key As CFStringRef) As CFStringRef
      Dim st As String = objectForKey(objct, ident)
      
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr
      
      // 列種別ごとに処理
      Dim pnt1 As Ptr
      select case kind
      case "text"  // 文字列
        
        // インスタンス生成
        pnt1 = NSClassFromString("NSTextField")  // クラスメソッドなので、まずNSTextFieldクラスを取得
        pnt1 = init(alloc(pnt1))
        Declare Sub setDelegate Lib "Cocoa" Selector "setDelegate:" (receiver As Ptr, obj As Ptr)
        setDelegate(pnt1, tableviewInst)
        // パラメーター
        declare sub setBordered lib "Cocoa" Selector "setBordered:" (obj as ptr, value as Boolean)
        setBordered(pnt1, false)
        Dim clr As Ptr = NSClassFromString("NSColor")
        declare function clearColor lib "Cocoa" Selector "clearColor" (obj as ptr) as ptr
        declare sub setBackgroundColor lib "Cocoa" Selector "setBackgroundColor:" (obj as ptr, col as ptr)
        setBackgroundColor(pnt1, clearColor(clr))
        declare function isEditable lib "Cocoa" Selector "isEditable" (id as ptr) as Boolean
        declare sub setEditable lib "Cocoa" Selector "setEditable:" (id as ptr, value as Boolean)
        setEditable(pnt1, isEditable(tableColumn))
        declare function myCell lib "Cocoa" Selector "cell" (id as ptr) as ptr
        declare sub setLineBreakMode lib "Cocoa" Selector "setLineBreakMode:" (id as ptr, mode as integer)
        setLineBreakMode(myCell(pnt1), 4)  // 4 = NSLineBreakByTruncatingTail
        // データ
        Declare Sub setStringValue Lib "Cocoa" Selector "setStringValue:" (receiver As Ptr, val As CFStringRef)
        setStringValue(pnt1, st)
        
      case "chkb"  // チェックボックス
        
        // インスタンス生成
        pnt1 = NSClassFromString("NSButton")  // クラスメソッドなので、まずNSButtonクラスを取得
        // macOS 10.0–10.14
        pnt1 = init(alloc(pnt1))
        Declare Sub setButtonType Lib "Cocoa" Selector "setButtonType:" (receiver As Ptr, val As UInteger)
        setButtonType(pnt1, 3)  // 3 = NSSwitchButton(チェックボックス)
        Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr)
        setTarget(pnt1, tableviewInst)  // Targetを設定
        Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr)
        setAction(pnt1, NSSelectorFromString("action:"))  // Actionを設定
        // macOS 10.12+(こちらを用いる場合は、RemoveCellView()でreleaseしないよう、処理を追加のこと)
        'Declare Function checkboxWithTitle Lib "Cocoa" Selector "checkboxWithTitle:target:action:" (receiver As Ptr, title As CFStringRef, tgt As Ptr, act As Ptr) As Ptr
        'pnt1 = checkboxWithTitle(pnt1, "", tableviewInst, NSSelectorFromString("action:"))  // ここでTarget,Actionも設定
        
        // パラメーター
        Declare Sub setAllowsMixedState Lib "Cocoa" Selector "setAllowsMixedState:" (receiver As Ptr, val As Boolean)
        setAllowsMixedState(pnt1, true)
        // データ
        Declare Sub setState Lib "Cocoa" Selector "setState:" (receiver As Ptr, val As Integer)
        setState(pnt1, Val(st))
        
      case "popu"  // ポップアップメニュー
        
        // インスタンス生成
        pnt1 = NSClassFromString("NSPopUpButton")  // クラスメソッドなので、まずNSPopUpButtonクラスを取得
        pnt1 = init(alloc(pnt1))
        // パラメーター
        Declare Sub bezelStyle Lib "Cocoa" Selector "setBezelStyle:" (receiver As Ptr, val As Integer)
        bezelStyle(pnt1, 11)  // NSBezelStyleTexturedRounded
        Declare Sub addItemsWithTitles Lib "Cocoa" selector "addItemsWithTitles:" (class_id As Ptr, menu As Ptr)
        addItemsWithTitles(pnt1, makeMenuArray())  // メニューを生成してセット
        Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr)
        setTarget(pnt1, tableviewInst)  // Targetを設定
        Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr)
        setAction(pnt1, NSSelectorFromString("action:"))  // Actionを設定
        // データ
        Declare Sub selectItemAtIndex Lib "Cocoa" Selector "selectItemAtIndex:" (receiver As Ptr, val As Integer)
        selectItemAtIndex(pnt1, Val(st))
        
      case "segm"  // セグメンテッドコントロール
        
        // インスタンス生成
        pnt1 = NSClassFromString("NSSegmentedControl")  // クラスメソッドなので、まずNSSegmentedControlクラスを取得
        pnt1 = init(alloc(pnt1))
        Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr)
        setTarget(pnt1, tableviewInst)  // Targetを設定
        Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr)
        setAction(pnt1, NSSelectorFromString("action:"))  // Actionを設定
        // パラメーター
        Declare Sub setSegmentStyle Lib "Cocoa" Selector "setSegmentStyle:" (receiver As Ptr, val As Integer)
        setSegmentStyle(pnt1, 3)  // NSSegmentStyleRoundRect
        Declare Sub setSegmentCount Lib "Cocoa" Selector "setSegmentCount:" (receiver As Ptr, cnt As Integer)
        setSegmentCount(pnt1, 3)  // セグメント数
        Declare Function width Lib "Cocoa" Selector "width" (receiver As Ptr) As CGFloat
        Dim w As CGFloat = width(tableColumn)  // 列幅を取得
        Declare Sub setWidth Lib "Cocoa" Selector "setWidth:forSegment:" (receiver As Ptr, w As CGFloat, no As Integer)
        setWidth(pnt1, (w-7)/3, 0)  // セグメントの幅を列幅の1/3に
        setWidth(pnt1, (w-7)/3, 1)
        setWidth(pnt1, (w-7)/3, 2)
        Declare Sub setLabel Lib "Cocoa" Selector "setLabel:forSegment:" (receiver As Ptr, w As CFStringRef, no As Integer)
        setLabel(pnt1, "1", 0)
        setLabel(pnt1, "2", 1)
        setLabel(pnt1, "3", 2)
        // データ
        Declare Sub setSelectedSegment Lib "Cocoa" Selector "setSelectedSegment:" (receiver As Ptr, val As Integer)
        setSelectedSegment(pnt1, Val(st))
        
      case "slid"  // スライダー
        
        // インスタンス生成
        pnt1 = NSClassFromString("NSSlider")  // クラスメソッドなので、まずNSSliderクラスを取得
        pnt1 = init(alloc(pnt1))
        Declare Sub setTarget Lib "Cocoa" Selector "setTarget:" (receiver As Ptr, actionTarget As Ptr)
        setTarget(pnt1, tableviewInst)  // Targetを設定
        Declare Sub setAction Lib "Cocoa" Selector "setAction:" (receiver As Ptr, actionEvent As Ptr)
        setAction(pnt1, NSSelectorFromString("action:"))  // Actionを設定
        // パラメーター
        Declare Sub setMinValue Lib "Cocoa" Selector "setMinValue:" (receiver As Ptr, val As Double)
        setMinValue(pnt1, 0.0)
        Declare Sub setMaxValue Lib "Cocoa" Selector "setMaxValue:" (receiver As Ptr, val As Double)
        setMaxValue(pnt1, 10.0)
        // データ
        Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double)
        setDoubleValue(pnt1, Val(st))
        
      case "indi"  // レベルインジケーター
        
        // インスタンス生成
        pnt1 = NSClassFromString("NSLevelIndicator")  // クラスメソッドなので、まずNSLevelIndicatorクラスを取得
        pnt1 = init(alloc(pnt1))
        // パラメーター
        Declare Sub setMinValue Lib "Cocoa" Selector "setMinValue:" (receiver As Ptr, val As Double)
        setMinValue(pnt1, 0.0)
        Declare Sub setMaxValue Lib "Cocoa" Selector "setMaxValue:" (receiver As Ptr, val As Double)
        setMaxValue(pnt1, 10.0)
        Declare Sub setWarningValue Lib "Cocoa" Selector "setWarningValue:" (receiver As Ptr, val As Double)
        setWarningValue(pnt1, 5.0)
        Declare Sub setCriticalValue Lib "Cocoa" Selector "setCriticalValue:" (receiver As Ptr, val As Double)
        setCriticalValue(pnt1, 9.0)
        // データ
        Declare Sub setDoubleValue Lib "Cocoa" Selector "setDoubleValue:" (receiver As Ptr, val As Double)
        setDoubleValue(pnt1, Val(st))
        
      end select
      
      // オブジェクトを返す
      return pnt1
    End Function
    
  29. 以下をWindow1にペースト(できなければNotesに、メモ名:File Spec.としてFile Spec.以降をコピー)
    File Spec.
    <入出力ファイルの仕様>
    
    ・1〜3行目は列情報、4行目以降がデータ
    
    ・列の区切り記号はカンマ(エスケープ処理はしていないので、カンマをテキストデータに含めることはできない)
    
    ・列情報
    1行目:列名
    2行目:列幅(実数扱い)
    3行目:列種別(テキスト=text、チェックボックス=chkb、ポップアップメニュー=popu、セグメンテッドコントロール=segm、スライダー=slid、レベルインジケーター=indi)
    
    ・記述例
    col 1,col 2,col 3
    150,150,150
    text,indi,popu
    0001,0,0
    …
    
    ・注意事項
     ・エラーチェックはしていないので、手作業で作成する場合は慎重に
    
  30. 以下をWindow1にペースト(できなければプロパティに、名前:dataArrayNS、データ型:Ptr、を追加)
    Protected Property dataArrayNS as Ptr
    
  31. 以下をWindow1にペースト(できなければプロパティに、名前:oldDescs、データ型:Ptr、を追加)
    Protected Property oldDescs as Ptr
    
  32. 以下をWindow1にペースト(できなければプロパティに、名前:previousCol、データ型:String、を追加)
    Protected Property previousCol as String
    
  33. 以下をWindow1にペースト(できなければプロパティに、名前:rowMax、データ型:Integer、を追加)
    Protected Property rowMax as Integer
    
  34. 以下をWindow1にペースト(できなければプロパティに、名前:tableviewInst、データ型:Ptr、を追加)
    Protected Property tableviewInst as Ptr
    
  35. 新規クラス(名前は、ここでは「NSViewListBox」)を作成。
  36. 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate、引数:id As Ptr, sel as CString, sender As Ptr、戻り値型:Ptr、を追加)
    Private Sub ActionDelegate(id As Ptr, sel as CString, sender As Ptr)
    
  37. 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate1、引数:id As Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer、戻り値型:Ptr、を追加)
    Private Function ActionDelegate1(id As Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer) as Ptr
    
  38. 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate2、引数:id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr、を追加)
    Private Sub ActionDelegate2(id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr)
    
  39. 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegate3、引数:id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr、を追加)
    Private Sub ActionDelegate3(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr)
    
  40. 以下をNSViewListBoxにペースト(できなければ移譲に、デリゲート名:ActionDelegateT、引数:id As Ptr, sel as CString, control As Ptr, fEditor As Ptr、戻り値型:Boolean、を追加)
    Private Function ActionDelegateT(id as Ptr, sel as CString, control As Ptr, fEditor As Ptr) as Boolean
    
  41. 以下をNSViewListBoxにペースト
    Public Sub Constructor(byRef inst As Ptr, win As Window, rect As NSRect, action As ActionDelegate, action1 As ActionDelegate1, action2 As ActionDelegate2, action3 As ActionDelegate3, actionT As ActionDelegateT)
      rect.y = win.Height - rect.y - rect.h // y座標系が、CocoaとXojoで反転しているので、補正
      
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr
      
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function initWithFrame Lib "Cocoa" selector "initWithFrame:" (obj_id As Ptr, frame As NSRect) As Ptr
      
      // NSTableViewを継承したカスタムクラスを作成。初回のみ
      makeClass()
      
      // NSTableViewのインスタンス作成
      Dim tableView As Ptr = initWithFrame(alloc(NSTableViewClass), NSMakeRect(0, 0, 0, 0))  // rectを指定すると、スクロール時に描画が乱れる(ヘッダーずれ他)
      Declare Sub setFocusRingType Lib "Cocoa" Selector "setFocusRingType:" (receiver As Ptr, typ As Integer)
      setFocusRingType(tableView, 1)  // 1 = NSFocusRingTypeNone(フォーカスリングなし)
      Declare Sub setUsesAlternatingRow Lib "Cocoa" Selector "setUsesAlternatingRowBackgroundColors:" (receiver As Ptr, flg As Boolean)
      setUsesAlternatingRow(tableView, true)  // 1行ごとに背景色を変える
      Declare Sub setRowHeight Lib "Cocoa" Selector "setRowHeight:" (receiver As Ptr, typ As CGFloat)
      setRowHeight(tableView, 22.0)  // 行の高さ
      Declare Sub setSelectionHighlightStyle Lib "Cocoa" Selector "setSelectionHighlightStyle:" (receiver As Ptr, typ As Integer)
      setSelectionHighlightStyle(tableView, -1)  // -1 = NSTableViewSelectionHighlightStyleNone(行のハイライト表示を行わない)
      Declare Sub setColumnAutoresizingStyle Lib "Cocoa" Selector "setColumnAutoresizingStyle:" (receiver As Ptr, typ As Integer)
      setColumnAutoresizingStyle(tableView, 0)  // 0 = NSTableViewNoColumnAutoresizing(列幅の自動リサイズを行わない)
      
      // NSScrollViewのインスタンス生成
      Dim scrollView As Ptr = NSClassFromString("NSScrollView")  // クラスメソッドなので、まずNSScrollViewクラスを取得
      scrollView = initWithFrame(alloc(scrollView), rect)
      Declare Sub setBorderType Lib "Cocoa" Selector "setBorderType:" (receiver As Ptr, typ As Integer)
      setBorderType(scrollView, 2)  // 2 = NSBezelBorder
      Declare Sub setHasVerticalScroller Lib "Cocoa" Selector "setHasVerticalScroller:" (receiver As Ptr, typ As Boolean)
      setHasVerticalScroller(scrollView, true)
      Declare Sub setHasHorizontalScroller Lib "Cocoa" Selector "setHasHorizontalScroller:" (receiver As Ptr, typ As Boolean)
      setHasHorizontalScroller(scrollView, true)
      Declare Sub setAutohidesScrollers Lib "Cocoa" Selector "setAutohidesScrollers:" (receiver As Ptr, typ As Boolean)
      setAutohidesScrollers(scrollView, true)
      Declare Sub setAutoresizingMask Lib "Cocoa" Selector "setAutoresizingMask:" (receiver As Ptr, typ As Integer)
      setAutoresizingMask(scrollView, 2+16)  // 2+16 = NSViewWidthSizable | NSViewHeightSizable
      
      // DataSource, Delegateの設定
      Declare Sub setDataSource Lib "Cocoa" Selector "setDataSource:" (receiver As Ptr, obj As Ptr)
      setDataSource(tableView, tableView)
      Declare Sub setDelegate Lib "Cocoa" Selector "setDelegate:" (receiver As Ptr, obj As Ptr)
      setDelegate(tableView, tableView)
      
      // スクロールビューにテーブルビューをセット
      Declare Sub setDocumentView Lib "Cocoa" Selector "setDocumentView:" (receiver As Ptr, obj As Ptr)
      setDocumentView(scrollView, tableView)
      
      // ウィンドウのビューを取得
      Declare Function contentView Lib "Cocoa" selector "contentView" (class_id As Integer) As Ptr
      Dim pnt2 As Ptr = contentView(win.handle)
      
      // スクロールビューをウィンドウのビューに追加
      Declare Sub addSubview Lib "Cocoa" selector "addSubview:" (class_id As Ptr, view As Ptr)
      addSubview(pnt2, scrollView)
      
      // テーブルビューのインスタンスを保持
      InstancePtr = tableView
      inst = InstancePtr
      
      // Window側でActionを受け取るメソッドを登録
      ActionHandler = action
      ActionHandler1 = action1
      ActionHandler2 = action2
      ActionHandler3 = action3
      ActionHandlerT = actionT
    End Sub
    
  42. 以下をNSViewListBoxにペースト(できなければプロパティに、名前:InstancePtr、データ型:Ptr、を追加)
    Private Property InstancePtr as Ptr
    
  43. 以下をNSViewListBoxにペースト
    Private Shared Sub makeClass()
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr
      
      // Declare宣言
      Declare Function objc_allocateClassPair Lib "Cocoa" (superclass As Ptr, name As CString, extraBytes As Integer) as Ptr
      Declare Sub objc_registerClassPair Lib "Cocoa" (cls As Ptr)
      Declare Function class_addMethod Lib "Cocoa" (cls As Ptr, name As Ptr, imp As Ptr, types As CString) As Boolean
      
      // 既にクラス作成済なら戻る
      if NSTableViewClass <> nil then
        return
      end if
      
      // クラス名をmyNSTableView、メタクラス名をNSTableViewにして、生成
      Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSTableView"), "myNSTableView", 0)
      // ランタイムに登録(参照を可能とするため)
      objc_registerClassPair newClassId
      
      // DataSourceの対象となるメソッドを追加(numberOfRowsInTableView:をXojo側で用意したmyNumberOfRowsメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("numberOfRowsInTableView:"), AddressOf myNumberOfRows, "i12@0:4@8") then
        msgBox "error1."
        return
      end if
      // DataSourceの対象となるメソッドを追加(tableView:sortDescriptorsDidChange:をXojo側で用意したmySortDescriptorsメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("tableView:sortDescriptorsDidChange:"), AddressOf mySortDescriptors, "v16@0:4@8@12") then
        msgBox "error2."
        return
      end if
      // Delegateの対象となるメソッドを追加(tableView:didClickTableColumn:をXojo側で用意したmyDidClickTableColumnメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("tableView:didClickTableColumn:"), AddressOf myDidClickTableColumn, "v16@0:4@8@12") then
        msgBox "error3."
        return
      end if
      // Delegateの対象となるメソッドを追加(tableView:viewForTableColumn:row:をXojo側で用意したmyViewForTableメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("tableView:viewForTableColumn:row:"), AddressOf myViewForTable, "@20@0:4@8@12i16") then
        msgBox "error4."
        return
      end if
      
      // Delegateの対象となるメソッドを追加(control:textShouldEndEditing:をXojo側で用意したmyEditTableメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("control:textShouldEndEditing:"), AddressOf myTextEndEdit, "@24@0:8@16") then
        msgBox "error5."
        return
      end if
      // Tarrgetに送られてきたActionの受け口となるメソッドを追加(action:をXojo側で用意したactionEventメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("action:"), AddressOf myActionEvent, "@@:@") then
        msgBox "error6."
        return
      end if
      
      // クラスを保持
      NSTableViewClass = newClassId
    End Sub
    
  44. 以下をNSViewListBoxにペースト
    Private Shared Sub myActionEvent(id As Ptr, SEL As CString, sender As Ptr)
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      ActionHandler.Invoke(id, sel, sender)
    End Sub
    
  45. 以下をNSViewListBoxにペースト
    Private Shared Sub myDidClickTableColumn(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr)
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      ActionHandler3.Invoke(id, sel, tableView, tableColumn)
    End Sub
    
  46. 以下をNSViewListBoxにペースト
    Private Shared Function myNumberOfRows(id as Ptr, sel as CString, tableView As Ptr) as Integer
      // 行数を返す(常に0行)
      return 0
    End Function
    
  47. 以下をNSViewListBoxにペースト
    Private Shared Sub mySortDescriptors(id as Ptr, sel as CString, tableView As Ptr, oldDescriptors As Ptr)
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      ActionHandler2.Invoke(id, sel, tableView, oldDescriptors)
    End Sub
    
  48. 以下をNSViewListBoxにペースト
    Private Shared Function myTextEndEdit(id as Ptr, sel as CString, control As Ptr, fEditor As Ptr) as Boolean
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandlerT.Invoke(id, sel, control, fEditor)
    End Function
    
  49. 以下をNSViewListBoxにペースト
    Private Shared Function myViewForTable(id as Ptr, sel as CString, tableView As Ptr, tableColumn As Ptr, rowidx As Integer) as Ptr
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler1.Invoke(id, sel, tableView, tableColumn, rowidx)
    End Function
    
  50. 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler、データ型:ActionDelegate、を追加)
    Private Shared Property ActionHandler as ActionDelegate
    
  51. 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler1、データ型:ActionDelegate1、を追加)
    Private Shared Property ActionHandler1 as ActionDelegate1
    
  52. 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler2、データ型:ActionDelegate2、を追加)
    Private Shared Property ActionHandler2 as ActionDelegate2
    
  53. 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandler3、データ型:ActionDelegate3、を追加)
    Private Shared Property ActionHandler3 as ActionDelegate3
    
  54. 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:ActionHandlerT、データ型:ActionDelegateT、を追加)
    Private Shared Property ActionHandlerT as ActionDelegateT
    
  55. 以下をNSViewListBoxにペースト(できなければ共有プロパティに、名前:NSTableViewClass、データ型:Ptr、を追加)
    Private Shared Property NSTableViewClass as Ptr
    
  56. 他に、NSMakeRect(メソッド)、NSRect(構造体)が必要ですが、macoslibからコピーさせて頂きました。(上記NSViewListBoxまたは別途モジュールを用意してコピーする。)
    注)macoslibではNSRectのメンバーの型にSingleが割り当てられているが、64bitにも対応したい場合は、CGFloatに書き換える。
 実行してみたところ、指定したコントロールがリストボックスのセルに追加され、機能することを確認しました。
S Shot1


 おわりに

 列の入れ替えは、プロパティで指定しているだけですが、入れ替え結果は直ちに列の並び順にも反映される等、こちらは何もしなくていいのは助かります。
 セグメンテッドコントロールは(以前のコラムでもそんな印象を持ちましたが)他と違うようで、他は幅を省略するとセル幅に追随するのですが、明示的にセットする必要がありました。(何か設定があるのでしょうか?)
 あと、テキストだけだと、どこまで行があるのかが分かかりずらいので、セル枠を描くなどした方がいいかもしれません。

 本格的に利用しようとすると、DataSource/Delegateメソッドの更なる追加や、Key-Value CodingやCocoa Bindingへの理解を深める等の必要もありそうで、結構大変そうです。


 お世話になったサイト

 貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)

 参考サイト(1):NSCellを使ってはいけない - Qiita
 参考サイト(2):NSTableView - src256 wiki
 参考サイト(3):NSTableView (Creation and Loading from Code) – Knowledge Stack


 更新履歴

 2021.04.20 Xojoでの実装の26項を改訂
 2019.07.11 改訂版・新規作成


[Home]  [MacSoft]  [Donation]  [History]  [Privacy Policy]  [Affiliate Policy]