ホームページ開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでアウトラインビューを使ってみる

 Xojo / Real Studio Trial and Error

CocoaのDeclareでアウトラインビューを使ってみる

目次
 はじめに

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

 以前、ちらっと話題にしたアウトラインビューの実装について、調べてみました。

 なお検証には、Xojo 2021 Release 2.1を用いています。(Mac mini 2018 + macOS 11.6 Big Sur)


 方針

 アウトラインビュー(NSOutlineView)はテーブルビュー(NSTableView)のサブクラスで、基本的な考え方は以前にやったこちらと同じです。
 この時は、全て自前でやったので、同じようなことをやれば所望の機能は得られると思われます。
 ですが、ここではアウトラインビュー(テーブルビューも)の特徴の一つである、Cocoa Bindingを利用してみることにします。

 ただし、Cocoa Bindingで検索して見つかる情報は、Interface Builderを使う方法が圧倒的で、例によってXojoでは使えません。
 それでも、以下が見つかりました。

 参考サイト(1):NSTreeController + NSOutlineView: A powerful combination | by Alexander Murphy | Building Ibotta | Medium
 参考サイト(2):NSTreeController + NSOutlineView:強力な組み合わせ(上記の日本語翻訳?)

 今回は、基本を理解するために不確定要素はできるだけ排除したいので、上記サンプルをそのまま移植してみることにしました。
(プロジェクトは、参考サイト(1)の「Just give me the code!」からダウンロード可)

 サンプルを眺めてみると、バインディングの部分は確かにコードなのですが、アウトラインビューの配置にはInterface Builderを使っています。
 このことは、カラム(デフォルトで2列配置済)、セル用のビュー、文字列用のテキストフィールドといった様々なものが「自動的に」セットされることを意味しますが、Xojoではこれら(と、プロジェクトを作成すると「自動的に」生成されるViewControllerも含めて)全てをコードで記述、となります。

 あと、サンプルではバインドの相手先としてプロパティを指定しているのですが、これ自体は初お目見えではあるものの、実装にはお馴染みのObjective-CのランタイムAPIを用います。
 ところがここで問題が。というのも、プロパティは引数に構造体の配列を指定するのですが、この方法が分からないという事態に。
 そこで、代わりにインスタンス変数を使ってみたところ、動作しているようでしたので、結局こちらにしました。
 なお、プロパティ/インスタンス変数は、宣言だけでなく、ゲッター/セッター(リードオンリーでは不要)メソッドも同時に記述します。

 参考サイト(3):objective c - How can I add properties to an object at runtime? - Stack Overflow
注:上記サイトの例では、セッターでの変数コピーにcopyを使っていますが、実験の結果、NSMutableArrayをセットすると(ゲッターでは)NSArray型で返ってきました。これでは書き換えができないので、copyの代わりにmutableCopyを使ったところ、NSMutableArray型で返ってくるようになりました。なので、以下の実装ではmutableCopyを使っていますが、これでいいのかはよく分かりません。

追記:セッターでは(上記サイトの通り)copyを使い、(編集可能なものは)ゲッターでmutable化する方が素直に思えてきました。
 以上を踏まえ、(残りの)仕様は以下の通りとしました。

 Xojoでの実装
【ソースコードのコピー&ペーストについて】
・ソースコード(グレー背景部分の全文)をコピーし、指定のオブジェクトにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
・ペーストはオブジェクトに行って下さい。オブジェクト内のEvent Handlers/Methods/Properties等にペーストしても、うまくいかない場合があります。
・それでもペーストできない場合は、各項目のカッコ内を適用して下さい。
  1. Xojoで新規プロジェクトを作成(Window1のWidthを480、Heightを270に変更)
  2. 以下をWindow1にペースト(できなければConstantsに、Constant Name:NSContentArrayBinding、Default Value:contentArray、Type:String、を追加)
    Protected Const NSContentArrayBinding as String = contentArray
    
  3. 以下をWindow1にペースト(できなければConstantsに、Constant Name:NSContentBinding、Default Value:content、Type:String、を追加)
    Protected Const NSContentBinding as String = content
    
  4. 以下をWindow1にペースト(できなければConstantsに、Constant Name:NSValueBinding、Default Value:value、Type:String、を追加)
    Protected Const NSValueBinding as String = value
    
  5. 以下をWindow1にペースト
    Sub Open() Handles Open
      // アウトラインビューとビューコントローラー初期化
      InitOutlineView()
      InitViewController()
      
      // nodeクラス初期化
      nodeClass = new Node()
      
      // データを生成してセット
      MakeOutline()
      
      // ウィンドウのTop補正(Big Surの場合のみ)
      me.Top=me.Top+8
    End Sub
    
  6. 以下をWindow1にペースト
    Protected Sub AddCol(title As String, width As CGFloat, ident As String)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 列クラス(NSTableColumn)のインスタンスを生成
      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)
      
      // 列幅をセット
      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, title As CFStringRef)
      setTitle(tCol, title)
      
      // 列クラスのインスタンスをアウトラインビューに追加
      Declare Sub addTableColumn Lib "Cocoa" Selector "addTableColumn:" (receiver As Ptr, col As Ptr)
      addTableColumn(outlineviewInst, tCol)
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(tCol)
    End Sub
    
  7. 以下をWindow1にペースト
    Protected Sub InitOutlineView()
      // NSOutlineView。引数は順に、生成したインスタンス(アウトラインビューとスクロールビュー)、位置/サイズ、Delegateメソッド
      Dim o As NSOutlineView = new NSOutlineView(outlineviewInst, scrollviewInst, NSMakeRect(0, 0, me.Width, me.Height), AddressOf viewForTableCol)
    End Sub
    
  8. 以下をWindow1にペースト
    Protected Sub InitViewController()
      // OutlineViewのController生成
      Dim c As NSViewController = new NSViewController(viewcntlInst)
      
      // ViewControllerに別途生成したOutlineView(実際にセットするのはScrollView)をセット
      Declare Sub setView Lib "Cocoa" Selector "setView:" (receiver As Ptr, view As Ptr)
      setView(viewcntlInst, scrollviewInst)
      
      // ウィンドウにViewControllerをセット
      Declare Sub setContentViewController Lib "Cocoa" Selector "setContentViewController:" (receiver As Integer, view As Ptr)
      setContentViewController(me.Handle, viewcntlInst)
    End Sub
    
  9. 以下をWindow1にペースト
    Protected Sub MakeOutline()
      // 二列生成
      AddCol("Nodes",320,"node")
      AddCol("Count",104,"count")
      
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // TreeController生成
      treecntlInst = NSClassFromString("NSTreeController")
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function init Lib "Cocoa" Selector "init" (receiver As Ptr) As Ptr
      treecntlInst = init(alloc(treecntlInst))
      Declare Sub setChildrenKeyPath Lib "Cocoa" Selector "setChildrenKeyPath:" (receiver As Ptr, kpath As CFStringRef)
      setChildrenKeyPath(treecntlInst, "children")
      Declare Sub setCountKeyPath Lib "Cocoa" Selector "setCountKeyPath:" (receiver As Ptr, cpath As CFStringRef)
      setCountKeyPath(treecntlInst, "count")
      Declare Sub setLeafKeyPath Lib "Cocoa" Selector "setLeafKeyPath:" (receiver As Ptr, kpath As CFStringRef)
      setLeafKeyPath(treecntlInst, "isLeaf")
      Declare Sub setObjectClass Lib "Cocoa" Selector "setObjectClass:" (receiver As Ptr, cls As Ptr)
      setObjectClass(treecntlInst, Node.GetNodeClass())
      
      // Cocoa Binding
      Declare Sub bind Lib "Cocoa" Selector "bind:toObject:withKeyPath:options:" (receiver As Ptr, bind As CFStringRef, obj As Ptr, key As CFStringRef, optn As Ptr)
      bind(treecntlInst, NSContentArrayBinding, viewcntlInst, "container", nil)  // TreeControllerのcontentArrayにViewControllerのcontainerをバインド
      bind(outlineviewInst, NSContentBinding, treecntlInst, "arrangedObjects", nil)  // OutlineviewのcontentにTreeControllerのarrangedObjectsをバインド
      
      // node生成
      Dim node1 As Ptr = setNode()
      
      // ViewControllerのcontainerにnodeをセット
      Declare Sub setContainer Lib "Cocoa" Selector "setContainer:" (receiver As Ptr, node As Ptr)
      setContainer(viewcntlInst, node1) 
    End Sub
    
  10. 以下をWindow1にペースト
    Protected Function SetNode() As Ptr
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
      
      // ルートノード
      Dim root As Ptr = NSClassFromString("NSMutableArray")
      Declare Function myArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr
      root = myArray(root)
      
      Dim ofr(2), tmp(0) As Ptr
      tmp(0) = nodeClass.MakeElement("💵 $0.24 back")  // リーフノードは名前のみ
      ofr(0) = nodeClass.MakeElement("🍦 Ice Cream",tmp)  // 枝ノードは名前と子供指定
      tmp(0) = nodeClass.MakeElement("💵 $0.75 back")  // リーフノードは名前のみ
      ofr(1) = nodeClass.MakeElement("☕️ Coffeem",tmp)  // 枝ノードは名前と子供指定
      tmp(0) = nodeClass.MakeElement("💵 $1.00 back")  // リーフノードは名前のみ
      ofr(2) = nodeClass.MakeElement("🍔 Burger",tmp)  // 枝ノードは名前と子供指定
      Dim ofrB As Ptr = nodeClass.MakeElement("💰 Offers",ofr)  // 枝ノードは名前と子供指定
      addObject(root, ofrB)  // ルートに追加
      
      Dim rtl(2) As Ptr
      rtl(0) = nodeClass.MakeElement("King Soopers")  // リーフノードは名前のみ
      rtl(1) = nodeClass.MakeElement("Walmart")  // リーフノードは名前のみ
      rtl(2) = nodeClass.MakeElement("Target")  // リーフノードは名前のみ
      Dim rtlB As Ptr = nodeClass.MakeElement("Retailers",rtl)  // 枝ノードは名前と子供指定
      addObject(root, rtlB)  // ルートに追加
      
      // clean up
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(ofrB)
      release(rtlB)
      
      return root
    End Function
    
  11. 以下をWindow1にペースト
    Private Function viewForTableCol(id As Ptr, sel As CString, outlineView As Ptr, tableColumn As Ptr, item As Ptr) As Ptr
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr
      Declare Sub setIdentifier Lib "Cocoa" Selector "setIdentifier:" (receiver As Ptr, ident As CFStringRef)
      
      // NSTableCellView生成
      Dim cellView As Ptr = NSClassFromString("NSTableCellView")
      
      Declare Function identifier Lib "Cocoa" selector "identifier" (class_id As Ptr) As CFStringRef
      Dim ident As CFStringRef = identifier(tableColumn)
      
      if ident="" then return cellView
      if ident="node" then  // 一列目
        Declare Function myDelegate Lib "Cocoa" selector "delegate" (class_id As Ptr) As Ptr
        Dim dele As Ptr = myDelegate(outlineView)
        
        // TableCellViewを取得(できなければ生成)
        Declare Function makeViewWithIdentifier Lib "Cocoa" Selector "makeViewWithIdentifier:owner:" (receiver As Ptr, ident As CFStringRef, own As Ptr) As Ptr
        Dim view As Ptr = makeViewWithIdentifier(outlineView, ident, dele)
        if view=nil then  // Create TableCellView
          view = NSClassFromString("NSTableCellView")
          view = initWithFrame(alloc(view), NSMakeRect(0,0,300,19))
          setIdentifier(view, ident)
        else  // 生成済ならそれを返す(そうしないと、二重書きが発生する)
          return view
        end if
        
        // TextField生成
        Dim textField As Ptr = NSClassFromString("NSTextField")
        Dim rect As NSRect = NSMakeRect(0,0,300,18)
        textField = initWithFrame(alloc(textField), rect)
        
        // パラメータセット
        Declare Sub setBezeled Lib "Cocoa" Selector "setBezeled:" (receiver As Ptr, flg As Boolean)
        setBezeled(textField, false)
        Declare Sub setEditable Lib "Cocoa" Selector "setEditable:" (receiver As Ptr, flg As Boolean)
        setEditable(textField, false)
        Declare Sub setDrawsBackground Lib "Cocoa" Selector "setDrawsBackground:" (receiver As Ptr, flg As Boolean)
        setDrawsBackground(textField, false)
        
        // TextFieldをTableCellViewに追加
        Declare Sub addSubview Lib "Cocoa" Selector "addSubview:" (receiver As Ptr, txt As Ptr)
        addSubview(view, textField)
        
        // clean up
        Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
        release(textField)
        
        // Cocoa Binding
        Declare Sub bind Lib "Cocoa" Selector "bind:toObject:withKeyPath:options:" (receiver As Ptr, bind As CFStringRef, obj As Ptr, key As CFStringRef, optn As Ptr)
        bind(textField, NSValueBinding, view, "objectValue.listitem", nil)  // textFieldのvalueにviewのobjectValue.listitemをバインド
        
        cellView = view
      elseif ident="count" then  // 二列目
        Declare Function myDelegate Lib "Cocoa" selector "delegate" (class_id As Ptr) As Ptr
        Dim dele As Ptr = myDelegate(outlineView)
        
        // TableCellViewを取得(できなければ生成)
        Declare Function makeViewWithIdentifier Lib "Cocoa" Selector "makeViewWithIdentifier:owner:" (receiver As Ptr, ident As CFStringRef, own As Ptr) As Ptr
        Dim view As Ptr = makeViewWithIdentifier(outlineView, ident, dele)
        if view=nil then  // Create TableCellView
          view = NSClassFromString("NSTableCellView")
          view = initWithFrame(alloc(view), NSMakeRect(0,0,100,19))
          setIdentifier(view, ident)
        else  // 生成済ならそれを返す(そうしないと、二重書きが発生する)
          return view
        end if
        
        // TextField生成
        Dim textField As Ptr = NSClassFromString("NSTextField")
        Dim rect As NSRect = NSMakeRect(0,0,100,18)
        textField = initWithFrame(alloc(textField), rect)
        
        // パラメータセット
        Declare Sub setBezeled Lib "Cocoa" Selector "setBezeled:" (receiver As Ptr, flg As Boolean)
        setBezeled(textField, false)
        Declare Sub setEditable Lib "Cocoa" Selector "setEditable:" (receiver As Ptr, flg As Boolean)
        setEditable(textField, false)
        Declare Sub setDrawsBackground Lib "Cocoa" Selector "setDrawsBackground:" (receiver As Ptr, flg As Boolean)
        setDrawsBackground(textField, false)
        
        // TextFieldをTableCellViewに追加
        Declare Sub addSubview Lib "Cocoa" Selector "addSubview:" (receiver As Ptr, txt As Ptr)
        addSubview(view, textField)
        
        // clean up
        Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
        release(textField)
        
        // Cocoa Binding
        Declare Sub bind Lib "Cocoa" Selector "bind:toObject:withKeyPath:options:" (receiver As Ptr, bind As CFStringRef, obj As Ptr, key As CFStringRef, optn As Ptr)
        bind(textField, NSValueBinding, view, "objectValue.countString", nil)  // textFieldのvalueにviewのobjectValue.countStringをバインド
        
        cellView = view
      else
        return cellView
      end if
      
      return cellView
    End Function
    
  12. 以下をWindow1にペースト(できなければPropertyに、Name:nodeClass、Type:Node、を追加)
    Protected Property nodeClass As Node
    
  13. 以下をWindow1にペースト(できなければPropertyに、Name:outlineviewInst、Type:Ptr、を追加)
    Protected Property outlineviewInst As Ptr
    
  14. 以下をWindow1にペースト(できなければPropertyに、Name:scrollviewInst、Type:Ptr、を追加)
    Protected Property scrollviewInst As Ptr
    
  15. 以下をWindow1にペースト(できなければPropertyに、Name:treecntlInst、Type:Ptr、を追加)
    Protected Property treecntlInst As Ptr
    
  16. 以下をWindow1にペースト(できなければPropertyに、Name:viewcntlInst、Type:Ptr、を追加)
    Protected Property viewcntlInst As Ptr
    
  17. 新規クラスを作成(名前は、ここでは「NSOutlineView」とした。)
  18. 以下をNSOutlineViewにペースト(できなければDelegateに、Delegate Name:ActionDelegate、Parameters:id As Ptr, sel As CString, outlineView As Ptr, tableColumn As Ptr, item As Ptr、Return Type:Ptr、を追加)
    Private Function ActionDelegate1(id As Ptr, sel As CString, outlineView As Ptr, tableColumn As Ptr, item As Ptr) As Ptr
    
  19. 以下をNSOutlineViewにペースト
    Public Sub Constructor(byRef inst As Ptr, byRef inst2 As Ptr, rect As NSRect, action1 As ActionDelegate1)
      // Window側でActionを受け取るメソッドを登録
      ActionHandler1 = action1
      
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName 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
      
      // NSOutlineViewを継承したカスタムクラスを作成。初回のみ
      makeClass()
      
      // NSOutlineViewのインスタンス作成
      Dim outlineView As Ptr = initWithFrame(alloc(NSOutlineViewClass), NSMakeRect(0, 0, 0, 0))  // rectを指定すると、スクロール時に描画が乱れる(ヘッダーずれ他)
      Declare Sub setFocusRingType Lib "Cocoa" Selector "setFocusRingType:" (receiver As Ptr, typ As Integer)
      setFocusRingType(outlineView, 1)  // 1 = NSFocusRingTypeNone(フォーカスリングなし)
      Declare Sub setGridStyleMask Lib "Cocoa" Selector "setGridStyleMask:" (receiver As Ptr, typ As Integer)
      setGridStyleMask(outlineView, 2)  //  2 = NSTableViewSolidHorizontalGridLineMask(実線の横方向罫線あり)
      Declare Sub setRowHeight Lib "Cocoa" Selector "setRowHeight:" (receiver As Ptr, typ As CGFloat)
      setRowHeight(outlineView, 19.0)  // 行の高さ
      Declare Sub setSelectionHighlightStyle Lib "Cocoa" Selector "setSelectionHighlightStyle:" (receiver As Ptr, typ As Integer)
      setSelectionHighlightStyle(outlineView, -1)  // -1 = NSTableViewSelectionHighlightStyleNone(行のハイライト表示を行わない)
      Declare Sub setAutoresizesOutlineColumn Lib "Cocoa" Selector "setAutoresizesOutlineColumn:" (receiver As Ptr, typ As Boolean)
      setAutoresizesOutlineColumn(outlineView, false)  // 列幅の自動リサイズを行わない
      
      // 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
      Declare Sub setDrawsBackground Lib "Cocoa" Selector "setDrawsBackground:" (receiver As Ptr, flg As Boolean)
      setDrawsBackground(scrollView, false)  // 背景を透明化
      
      // Delegateの設定
      Declare Sub setDelegate Lib "Cocoa" Selector "setDelegate:" (receiver As Ptr, obj As Ptr)
      setDelegate(outlineView, outlineView)
      
      // スクロールビューにアウトラインビューをセット
      Declare Sub setDocumentView Lib "Cocoa" Selector "setDocumentView:" (receiver As Ptr, obj As Ptr)
      setDocumentView(scrollView, outlineView)
      
      // インスタンスを保持
      InstancePtr = outlineView
      inst = InstancePtr
      inst2 = scrollView
    End Sub
    
  20. 以下をNSOutlineViewにペースト(できなければPropertyに、Name:InstancePtr、Type:Ptr、を追加)
    Private Property InstancePtr As Ptr
    
  21. 以下をNSOutlineViewにペースト
    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 NSOutlineViewClass <> nil then
        return
      end if
      
      // クラス名をmyNSOutlineView、メタクラス名をNSOutlineViewにして、生成
      Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSOutlineView"), "myNSOutlineView", 0)
      // ランタイムに登録(参照を可能とするため)
      objc_registerClassPair newClassId
      
      // Delegateの対象となるメソッドを追加(outlineView:viewForTableColumn:item:をXojo側で用意したmyViewForTableColumnメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("outlineView:viewForTableColumn:item:"), AddressOf myViewForTableColumn, "@32@0:8@16p24") then
        msgBox "error1."
        return
      end if
      
      // クラスを保持
      NSOutlineViewClass = newClassId
    End Sub
    
  22. 以下をNSOutlineViewにペースト
    Private Shared Function myViewForTableColumn(id As Ptr, sel As CString, outlineView As Ptr, tableColumn As Ptr, item As Ptr) As Ptr
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler1.Invoke(id, sel, outlineView, tableColumn, item)
    End Function
    
  23. 以下をNSOutlineViewにペースト(できなければShared Propertyに、Name:ActionHandler1、Type:ActionDelegate1、を追加)
    Private Shared Property ActionHandler1 As ActionDelegate1
    
  24. 以下をNSOutlineViewにペースト(できなければShared Propertyに、Name:NSOutlineViewClass、Type:Ptr、を追加)
    Private Shared Property NSOutlineViewClass As Ptr
    
  25. 新規クラスを作成(名前は、ここでは「NSViewController」とした。)
  26. 以下をNSViewControllerにペースト(できなければDelegateに、Delegate Name:ActionDelegate1、Parameters:id As Ptr, sel As CString、Return Type:Ptr、を追加)
    Private Function ActionDelegate1(id As Ptr, sel As CString) As Ptr
    
  27. 以下をNSViewControllerにペースト(できなければDelegateに、Delegate Name:ActionDelegate2、Parameters:id As Ptr, sel As CString, value As Ptr、を追加)
    Private Sub ActionDelegate2(id As Ptr, sel As CString, value As Ptr)
    
  28. 以下をNSViewControllerにペースト
    Public Sub Constructor(byRef inst As Ptr)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // NSViewControllerを継承したカスタムクラスを作成。初回のみ
      makeClass()
      
      // NSViewControllerのインスタンス作成
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function initWithNibName Lib "Cocoa" Selector "initWithNibName:bundle:" (receiver As Ptr, name As Ptr, bundle As Ptr) As Ptr
      Dim viewCntl As Ptr = initWithNibName(alloc(NSViewControllerClass), nil, nil)
      
      // インスタンスを保持
      InstancePtr = viewCntl
      inst = InstancePtr
      
      // インスタンス側でActionを受け取るメソッドを登録
      ActionHandler1 = addressOf getContainer
      ActionHandler2 = addressOf setContainer
    End Sub
    
  29. 以下をNSViewControllerにペースト
    Private Function getContainer(id As Ptr, sel As CString) As Ptr
      // クラスから変数(container)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, cnt As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSViewControllerClass, "container")
      
      // インスタンスから変数(container)の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim value As Ptr = object_getIvar(id, ivar)
      
      return value
    End Function
    
  30. 以下をNSViewControllerにペースト
    Private Sub setContainer(id As Ptr, sel As CString, value As Ptr)
      // クラスから変数(container)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, name As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSViewControllerClass, "container")
      
      // インスタンスから変数(container)の現在の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim oldValue As Ptr = object_getIvar(id, ivar)
      
      // 値が異なればセット
      if value<>oldValue then
        
        // インスタンスの変数(container)に値をセット
        Declare Sub object_setIvar Lib "Cocoa" (superclass As Ptr, ivar as Ptr, val as Ptr)
        Declare Function mutableCopy Lib "Cocoa" Selector "mutableCopy" (receiver As Ptr) As Ptr
        object_setIvar(id, ivar, mutableCopy(value))  // コピーをセットしないと、オリジナルが解放されてしまう?
        
      end if
    End Sub
    
  31. 以下をNSViewControllerにペースト(できなければPropertyに、Name:InstancePtr、Type:Ptr、を追加)
    Private Property InstancePtr As Ptr
    
  32. 以下をNSViewControllerにペースト
    Private Shared Function containerGetter(id As Ptr, sel As CString) As Ptr
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler1.Invoke(id, sel)
    End Function
    
  33. 以下をNSViewControllerにペースト
    Private Shared Sub containerSetter(id As Ptr, sel As CString, value As Ptr)
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      ActionHandler2.Invoke(id, sel, value)
    End Sub
    
  34. 以下をNSViewControllerにペースト
    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
      Declare Function class_addIvar Lib "Cocoa" (cls as Ptr, name as CString, size as UInteger, alignment as UInt8, types as CString) As Boolean
      
      // 既にクラス作成済なら戻る
      if NSViewControllerClass <> nil then
        return
      end if
      
      // クラス名をmyNSViewController、メタクラス名をNSViewControllerにして、生成
      Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSViewController"), "myNSViewController", 0)
      
      // 変数を追加(container)
      if not class_addIvar (newClassId, "container", 8, 3, "@") then
        msgBox "error1."
        return
      end if
      
      // ランタイムに登録(参照を可能とするため)
      objc_registerClassPair newClassId
      
      // メソッドを追加(containerをXojo側で用意したtypeGetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("container"), AddressOf containerGetter, "@@:") then
        msgBox "error2."
        return
      end if
      // メソッドを追加(setContainer:をXojo側で用意したtypeSetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("setContainer:"), AddressOf containerSetter, "v@:@") then
        msgBox "error3."
        return
      end if
      
      // クラスを保持
      NSViewControllerClass = newClassId
    End Sub
    
  35. 以下をNSViewControllerにペースト(できなければShared Propertyに、Name:ActionHandler1、Type:ActionDelegate1、を追加)
    Private Shared Property ActionHandler1 As ActionDelegate1
    
  36. 以下をNSViewControllerにペースト(できなければShared Propertyに、Name:ActionHandler2、Type:ActionDelegate2、を追加)
    Private Shared Property ActionHandler2 As ActionDelegate2
    
  37. 以下をNSViewControllerにペースト(できなければShared Propertyに、Name:NSViewControllerClass、Type:Ptr、を追加)
    Private Shared Property NSViewControllerClass As Ptr
    
  38. 新規クラスを作成(名前は、ここでは「Node」とした。)
  39. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate1、Parameters:id As Ptr, sel As CString、Return Type:CFStringRef、を追加)
    Private Function ActionDelegate1(id As Ptr, sel As CString) As CFStringRef
    
  40. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate2、Parameters:id As Ptr, sel As CString, newName As Ptr、を追加)
    Private Sub ActionDelegate2(id As Ptr, sel As CString, newName As Ptr)
    
  41. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate3、Parameters:id As Ptr, sel As CString、Return Type:Ptr、を追加)
    Private Function ActionDelegate3(id As Ptr, sel As CString) As Ptr
    
  42. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate4、Parameters:id As Ptr, sel As CString, newName As Ptr、を追加)
    Private Sub ActionDelegate4(id As Ptr, sel As CString, newName As Ptr)
    
  43. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate5、Parameters:id As Ptr, sel As CString、Return Type:Integer、を追加)
    Private Function ActionDelegate5(id As Ptr, sel As CString) As Integer
    
  44. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate6、Parameters:id As Ptr, sel As CString、Return Type:Boolean、を追加)
    Private Function ActionDelegate6(id As Ptr, sel As CString) As Boolean
    
  45. 以下をNodeにペースト(できなければDelegateに、Delegate Name:ActionDelegate7、Parameters:id As Ptr, sel As CString、Return Type:Ptr、を追加)
    Private Function ActionDelegate7(id As Ptr, sel As CString) As Ptr
    
  46. 以下をNodeにペースト
    Public Sub Constructor()
      // NSObjectを継承したカスタムクラスを作成。初回のみ
      makeClass()
      
      // インスタンス側でActionを受け取るメソッドを登録
      ActionHandler1 = addressOf getListitem
      ActionHandler2 = addressOf setListitem
      
      ActionHandler3 = addressOf getChildren
      ActionHandler4 = addressOf setChildren
      
      ActionHandler5 = addressOf getCount
      ActionHandler6 = addressOf getIsLeaf
      ActionHandler7 = addressOf getChildrenCount
    End Sub
    
  47. 以下をNodeにペースト
    Private Function getChildren(id As Ptr, sel As CString) As Ptr
      // クラスから変数(children)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, cnt As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "children")
      
      // インスタンスから変数(children)の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim value As Ptr = object_getIvar(id, ivar)
      
      return value
    End Function
    
  48. 以下をNodeにペースト
    Private Function getChildrenCount(id As Ptr, sel As CString) As Ptr
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // クラスから変数(children)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, cnt As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "children")
      
      // インスタンスから変数(children)の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim value As Ptr = object_getIvar(id, ivar)
      
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim cnt As Integer = count(value)
      
      if cnt<=0 then
        return nil
      end if
      
      Dim stt As String
      if cnt=1 then
        stt = Str(cnt)+" node"
      else
        stt = Str(cnt)+" nodes"
      end if
      
      Dim st As Ptr = NSClassFromString("NSString")
      Declare Function stringWithUTF8String Lib "Cocoa" Selector "stringWithUTF8String:" (receiver As Ptr, str As CString) As Ptr
      st = stringWithUTF8String(st, stt)
      
      return st
    End Function
    
  49. 以下をNodeにペースト
    Private Function getCount(id As Ptr, sel As CString) As Integer
      // クラスから変数(children)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, cnt As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "children")
      
      // インスタンスから変数(children)の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim value As Ptr = object_getIvar(id, ivar)
      
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim value2 As Integer = count(value)
      
      return value2
    End Function
    
  50. 以下をNodeにペースト
    Private Function getIsLeaf(id As Ptr, sel As CString) As Boolean
      // クラスから変数(children)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, cnt As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "children")
      
      // インスタンスから変数(children)の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim value As Ptr = object_getIvar(id, ivar)
      
      // childrenの数を取得
      Declare Function count Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer
      Dim value2 As Integer = count(value)
      
      // childrenを持っていなければtrue、持っていればfalseを返す
      if value2=0 then
        return true
      else
        return false
      end if
    End Function
    
  51. 以下をNodeにペースト
    Private Function getListitem(id As Ptr, sel As CString) As CFStringRef
      // クラスから変数(listitem)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, cnt As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "listitem")
      
      // インスタンスから変数(listitem)の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as CFStringRef
      Dim value As String = object_getIvar(id, ivar)
      
      return value
    End Function
    
  52. 以下をNodeにペースト
    Public Function MakeElement(Listitem As String) As Ptr
      return MakeElement(Listitem,nil)
    End Function
    Public Function MakeElement(Listitem As String, Child() As Ptr) As Ptr
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // Nodeのインスタンス生成
      Dim inst As Ptr=NewInstance()
      
      // タイトルをセット
      Declare Sub setListitem Lib "Cocoa" Selector "setListitem:" (receiver As Ptr, value As CFStringRef)
      setListitem(inst, Listitem)
      
      // 空の子供配列を生成
      Dim ary As Ptr = NSClassFromString("NSMutableArray")
      Declare Function myArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr
      ary = myArray(ary)
      
      // 子供をセット
      if Child=nil then  // リーフはnilが渡ってくるので、空の子供配列をそのままセット
        
        Declare Sub setChildren Lib "Cocoa" Selector "setChildren:" (receiver As Ptr, value As Ptr)
        setChildren(inst, ary)
        
      else
        
        // 渡ってきた子供を子供配列に追加
        for i As Integer = 0 to Child.Ubound
          Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
          addObject(ary, Child(i))
          
          // clean up
          Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
          release(Child(i))
        next
        
        Declare Sub setChildren Lib "Cocoa" Selector "setChildren:" (receiver As Ptr, value As Ptr)
        setChildren(inst, ary)
        
      end if
      
      // インスタンスを返す
      return inst
    End Function
    
  53. 以下をNodeにペースト
    Private Function NewInstance() As Ptr
      // インスタンスを作成して返す
      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
      return init(alloc(NSNodeClass))
    End Function
    
  54. 以下をNodeにペースト
    Private Sub setChildren(id As Ptr, sel As CString, value As Ptr)
      // クラスから変数(children)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, name As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "children")
      
      // インスタンスから変数(children)の現在の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim oldValue As Ptr = object_getIvar(id, ivar)
      
      // 値が異なればセット
      if value<>oldValue then
        
        // インスタンスの変数(children)に値をセット
        Declare Sub object_setIvar Lib "Cocoa" (superclass As Ptr, ivar as Ptr, str as Ptr)
        Declare Function mutableCopy Lib "Cocoa" Selector "mutableCopy" (receiver As Ptr) As Ptr
        object_setIvar(id, ivar, mutableCopy(value))  // コピーをセットしないと、オリジナルが解放されてしまう?
        
      end if
    End Sub
    
  55. 以下をNodeにペースト
    Private Sub setListitem(id As Ptr, sel As CString, value As Ptr)
      // クラスから変数(listitem)のIVerを取得
      Declare Function class_getInstanceVariable Lib "Cocoa" (cls As Ptr, name As CString) As Ptr
      Dim ivar As Ptr = class_getInstanceVariable(NSNodeClass, "listitem")
      
      // インスタンスから変数(listitem)の現在の値を取得
      Declare Function object_getIvar Lib "Cocoa" (superclass As Ptr, extraBytes As Ptr) as Ptr
      Dim oldValue As Ptr = object_getIvar(id, ivar)
      
      // 値が異なればセット
      if value<>oldValue then
        
        // インスタンスの変数(listitem)に値をセット
        Declare Sub object_setIvar Lib "Cocoa" (superclass As Ptr, ivar as Ptr, str as Ptr)
        Declare Function mutableCopy Lib "Cocoa" Selector "mutableCopy" (receiver As Ptr) As Ptr
        object_setIvar(id, ivar, mutableCopy(value))  // コピーをセットしないと、オリジナルが解放されてしまう?
        
      end if
    End Sub
    
  56. 以下をNodeにペースト(できなければPropertyに、Name:InstancePtr、Type:Ptr、を追加)
    Public Property InstancePtr As Ptr
    
  57. 以下をNodeにペースト
    Private Shared Function chCountGetter(id As Ptr, sel As CString) As Ptr
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler7.Invoke(id, sel)
    End Function
    
  58. 以下をNodeにペースト
    Private Shared Function childrenGetter(id As Ptr, sel As CString) As Ptr
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler3.Invoke(id, sel)
    End Function
    
  59. 以下をNodeにペースト
    Private Shared Sub childrenSetter(id As Ptr, sel As CString, value As Ptr)
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      ActionHandler4.Invoke(id, sel, value)
    End Sub
    
  60. 以下をNodeにペースト
    Private Shared Function countGetter(id As Ptr, sel As CString) As Integer
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler5.Invoke(id, sel)
    End Function
    
  61. 以下をNodeにペースト
    Public Shared Function GetNodeClass() As Ptr
      return NSNodeClass
    End Function
    
  62. 以下をNodeにペースト
    Private Shared Function isLeafGetter(id As Ptr, sel As CString) As Boolean
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler6.Invoke(id, sel)
    End Function
    
  63. 以下をNodeにペースト
    Private Shared Function listitemGetter(id As Ptr, sel As CString) As CFStringRef
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      return ActionHandler1.Invoke(id, sel)
    End Function
    
  64. 以下をNodeにペースト
    Private Shared Sub listitemSetter(id As Ptr, sel As CString, value As Ptr)
      // Constructorで登録した、Actionを受け取るメソッドを呼び出す
      ActionHandler2.Invoke(id, sel, value)
    End Sub
    
  65. 以下をNodeにペースト
    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
      Declare Function class_addIvar Lib "Cocoa" (cls as Ptr, name as CString, size as UInteger, alignment as UInt8, types as CString) As Boolean
      
      // 既にクラス作成済なら戻る
      if NSNodeClass <> nil then
        return
      end if
      
      // クラス名をmyNSNode、メタクラス名をNSObjectにして、生成
      Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSObject"), "myNSNode", 0)
      
      // 変数を追加(listitem)
      if not class_addIvar (newClassId, "listitem", 8, 3, "@") then
        msgBox "error1."
        return
      end if
      // 変数を追加(children)
      if not class_addIvar (newClassId, "children", 8, 3, "@") then
        msgBox "error2."
        return
      end if
      
      // ランタイムに登録(参照を可能とするため)
      objc_registerClassPair newClassId
      
      // メソッドを追加(listitemをXojo側で用意したlistitemGetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("listitem"), AddressOf listitemGetter, "@@:") then
        msgBox "error3."
        return
      end if
      // メソッドを追加(setListitem:をXojo側で用意したlistitemSetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("setListitem:"), AddressOf listitemSetter, "v@:@") then
        msgBox "error4."
        return
      end if
      // メソッドを追加(childrenをXojo側で用意したchildrenGetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("children"), AddressOf childrenGetter, "@@:") then
        msgBox "error5."
        return
      end if
      // メソッドを追加(setChildren:をXojo側で用意したchildrenSetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("setChildren:"), AddressOf childrenSetter, "v@:@") then
        msgBox "error6."
        return
      end if
      // メソッドを追加(isLeafをXojo側で用意したisLeafGetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("isLeaf"), AddressOf isLeafGetter, "B@:") then
        msgBox "error7."
        return
      end if
      // メソッドを追加(countをXojo側で用意したcountGetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("count"), AddressOf countGetter, "i@:") then
        msgBox "error8."
        return
      end if
      // メソッドを追加(childrenCountをXojo側で用意したchCountGetterメソッドで受け取る。)
      if not class_addMethod (newClassId, NSSelectorFromString("countString"), AddressOf chCountGetter, "@@:") then
        msgBox "error8."
        return
      end if
      
      // クラスを保持
      NSNodeClass = newClassId
    End Sub
    
  66. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler1、Type:ActionDelegate1、を追加)
    Private Shared Property ActionHandler1 As ActionDelegate1
    
  67. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler2、Type:ActionDelegate2、を追加)
    Private Shared Property ActionHandler2 As ActionDelegate2
    
  68. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler3、Type:ActionDelegate3、を追加)
    Private Shared Property ActionHandler3 As ActionDelegate3
    
  69. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler4、Type:ActionDelegate4、を追加)
    Private Shared Property ActionHandler4 As ActionDelegate4
    
  70. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler5、Type:ActionDelegate5、を追加)
    Private Shared Property ActionHandler5 As ActionDelegate5
    
  71. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler6、Type:ActionDelegate6、を追加)
    Private Shared Property ActionHandler6 As ActionDelegate6
    
  72. 以下をNodeにペースト(できなければShared Propertyに、Name:ActionHandler7、Type:ActionDelegate7、を追加)
    Private Shared Property ActionHandler7 As ActionDelegate7
    
  73. 以下をNodeにペースト(できなければShared Propertyに、Name:NSNodeClass、Type:Ptr、を追加)
    Private Shared Property NSNodeClass As Ptr
    
  74. 他に、NSMakeRect(メソッド)、NSRect(構造体)が必要ですが、それらはmacoslibからコピーさせて頂きました。(別途モジュールを用意してコピーする。)
    注)macoslibではNSMakeRectの引数、NSRectのメンバーの型にSingleが割り当てられているが、64bitに対応するため、CGFloatに書き換える。
 実行してみたところ、サンプルと同等な結果が得られることを確認しました。
S Shot1


 おわりに

 Cocoa Bindingを利用することで、outlineView:viewForTableColumn:item:以外のDelegate/DataSourceを意識しなくて済むのは楽です。

 それから、描画時のバインディングは、とりあえず動作しているようなのでそのまま使ってしまいましたが、実は今ひとつピンときていません。
 というのも、バインド先はセルビューなのですが、バインドする変数/メソッドはセルビューではなく、ビューコントローラーからアクセスできるものです。
 オリジナルがアウトラインビューからセルビューを呼び出しているのに対し、自作ではセルビューをその場で生成しているので、この時点では両者は紐づけられていません。
 それでも機能するということは、セルビューを返した後に紐づけられるとしか考えられませんが、その解釈でいいのかは分かりません。

 あと、メモリーリークのチェックにLeaksを使おうとしたのですが、エラーが出て使えなくなっていました。
 調べたら、以下のサイトに解決法が示されていました。
 実行の結果、Leaksでのメモリーリーク検出はありませんでした。

 参考サイト(4):M1. Instrument Leaks error "Failed… | Apple Developer Forums


 お世話になったサイト

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

 参考サイト(1):NSTreeController + NSOutlineView: A powerful combination | by Alexander Murphy | Building Ibotta | Medium
 参考サイト(2):NSTreeController + NSOutlineView:強力な組み合わせ(上記の日本語翻訳?)
 参考サイト(3):objective c - How can I add properties to an object at runtime? - Stack Overflow
 参考サイト(4):M1. Instrument Leaks error "Failed… | Apple Developer Forums


 更新履歴

 2022.03.08 方針に、追記を追加。
 2021.10.08 新規作成


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