NSOutlineView with alert dialog

We made a lot of “miniature garden” interface on the alert dialog. But NSOutlineView remains. Does somobody has an idea to use NSOutlineView on alert dialog.

If we can use NSOutlineView with alert dialog, we can select multi-leveled data easily.

–checkbox
http://piyocast.com/as/archives/8067

–path control
http://piyocast.com/as/archives/7140

–popup menu
http://piyocast.com/as/archives/8046

–date picker
http://piyocast.com/as/archives/7713

–WkWebView
http://piyocast.com/as/archives/8120

–Table View
http://piyocast.com/as/archives/7295

–NSBrowser + Map view
http://piyocast.com/as/archives/5820

My trial code is here.


-- Created 2019-02-23 by Takaaki Naganoya
-- 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application's NSAlert
property NSIndexSet : a reference to current application's NSIndexSet
property NSScrollView : a reference to current application's NSScrollView
property NSTableView : a reference to current application's NSTableView
property NSTableColumn : a reference to current application's NSTableColumn
property NSMutableArray : a reference to current application's NSMutableArray
property NSTreeController : a reference to current application's NSTreeController
property NSRunningApplication : a reference to current application's NSRunningApplication
property NSSegmentedControl : a reference to current application's NSSegmentedControl
property NSAlertSecondButtonReturn : a reference to current application's NSAlertSecondButtonReturn
property NSSegmentStyleTexturedRounded : a reference to current application's NSSegmentStyleTexturedRounded

property aMaxViewWidth : 800
property theResult : 0

property rootDirectory : ""
property rootDirectoryItems : {}
property allChildItems : {}
property filtered_list : {}

property aTree : missing value --NSTreeViewController

set paramObj to {myMessage:"Choose one line", mySubMessage:"Select one line you want"}
--my chooseItemByOutlineView:paramObj--for debugging
my performSelectorOnMainThread:"chooseItemByOutlineView:" withObject:(paramObj) waitUntilDone:true

if (my theResult) = 0 then error number -128
return (my theResult)

on chooseItemByOutlineView:paramObj
	set aMainMes to myMessage of paramObj
	set aSubMes to mySubMessage of paramObj
	
	set aWidth to 600
	set aHeight to 400
	
	--Make OutlineView+Scroll View
	set aScroll to current application's NSScrollView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, aWidth, aHeight))
	set aView to current application's NSOutlineView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, aWidth, aHeight))
	aScroll's setDocumentView:aView
	
	aView's setDelegate:me
	aView's setIndentationPerLevel:(16 as real)
	aView's setIndentationMarkerFollowsCell:true
	
	set level1 to {"l1a", "l1b", "l1c", "l1d"}
	set level2 to {"l1a/l2a", "l1a/l2c", "l1a/l2e"}
	set level3 to {"l1a/l2a/l3f", "l1a/l2a/l3g", "l1a/l2e/l3a"}
	
	set aTree to NSTreeController's alloc()'s init()
	set optRec to current application's NSMutableDictionary's dictionaryWithObjects:{true, true} forKeys:{current application's NSRaisesForNotApplicableKeysBindingOption, current application's NSConditionallySetsEditableBindingOption}
	--aTree's bind:"contentArray" toObject:(me) withKeyPath:"treeContents" options:(optRec) --????
	
	set rootDirectoryItems to level1
	set allChildItems to level2 & level3
	aView's setDataSource:allChildItems
	
	-- set up alert	
	set theAlert to NSAlert's alloc()'s init()
	tell theAlert
		its setMessageText:aMainMes
		its setInformativeText:aSubMes
		its addButtonWithTitle:"OK"
		its addButtonWithTitle:"Cancel"
		its setAccessoryView:aScroll
	end tell
	
	-- show alert in modal loop
	NSRunningApplication's currentApplication()'s activateWithOptions:0
	my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
	
end chooseItemByOutlineView:

on doModal:aParam
	set (my returnCode) to aParam's runModal()
end doModal:



-- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
on outlineView:outlineView child:theChild ofItem:outlineItem
	display dialog "1"
	set childItem to ""
	try
		-- theChild is an index to the rootDirectoryItems list
		if outlineItem is equal to missing value then
			--  Note the rootDirectoryItems list is one based
			set childItem to (get item (theChild + 1) of rootDirectoryItems) as string
			
		else
			-- create filtered_list containg items starting with outlineitem
			set filtered_list to {}
			myfilter(allChildItems, outlineItem) of me
			
			--theChild is zero based and filtered_list is one based
			set childItem to (get item (theChild + 1) of filtered_list) as string
		end if
	end try
	
	(* Uncomment for debugging purposes *)
	--list2text(rootDirectoryItems)
	return childItem
end outlineView:child:ofItem:


-- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
on outlineView:outlineView objectValueForTableColumn:tableColumn byItem:outlineItem
	display dialog "2"
	-- itemValue defaults to rootDirectory
	set itemValue to "default"
	
	--try
	--    set itemValue to outlineItem as string
	--end try
	
	-- Note Applescript's is a required keyword 
	-- for the delimiting procedure to work properly
	set AppleScript's text item delimiters to {"/"}
	try
		-- last text item    
		set str_outlineItem to outlineItem as string
		set itemValue to (text item -1) of str_outlineItem
	end try
	-- revert to default state
	set AppleScript's text item delimiters to ""
	
	(* Uncomment for debugging purposes *)
	--log "2 the itemvalue is " & itemValue
	--display alert "2 the itemValue is " & itemValue
	
	return itemValue
	
end outlineView:objectValueForTableColumn:byItem:

-- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
on outlineView:outlineView numberOfChildrenOfItem:outlineItem
	set itemCount to 0
	try
		if (count of rootDirectoryItems) is greater than 0 then
			if outlineItem is equal to missing value then
				-- one extra spot for the rootDirectory entry
				set itemCount to (count of rootDirectoryItems) + 1
			else
				-- create filtered_list containg items starting with outlineitem
				myfilter(allChildItems, outlineItem)
				-- count of filtered_list items
				set itemCount to (count of filtered_list)
			end if
		end if
	end try
	return itemCount
end outlineView:numberOfChildrenOfItem:


-- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
on outlineView:outlineView isItemExpandable:outlineItem
	set isExpandable to false
	try
		if outlineItem is missing value then
			if (length of rootDirectoryItems) is greater than 1 then
				set isExpandable to true
			end if
		else
			-- create filtered_list containg items starting with outlineitem
			set filtered_list to {}
			myfilter(allChildItems, outlineItem)
			--log filtered_list as text
			if (count of filtered_list) is greater than 0 then
				set isExpandable to true
			end if
		end if
	end try
	return isExpandable
end outlineView:isItemExpandable:


on outlineView:aView shouldExpandItem:outlineItem
	return true
end outlineView:shouldExpandItem:

on outlineView:aView shouldCollapseItem:outlineItem
	return true
end outlineView:shouldCollapseItem:

on outlineView:aView shouldSelectItem:outlineItem
	return true
end outlineView:shouldSelectItem:

on outlineView:aView shouldSelectTableColumn:outlineItem
	return true
end outlineView:shouldSelectTableColumn:


-- utility subroutines 
-- convert a list into a string for display purposes *) 
on list2text(theList)
	if theList is missing value then
		set var1 to "The list is empty"
	else
		set var1 to ""
		-- count of the list items   
		repeat with i from 1 to length of theList
			set var to item i of theList
			-- save var1
			set var2 to var1
			set var1 to var2 & "[" & var & "]"
		end repeat
	end if
	--display alert var1 message "The list is shown above"
end list2text


-- Filter the srcList to retain items that are children of thisItem
-- write results to filtered_list
on myfilter(srcList, thisItem)
	set filtered_list to {}
	
	repeat with i from 1 to (count of srcList)
		
		set itemx to item i of srcList
		-- derive theParent of thisItem
		if itemx contains "/" then
			-- Note Applescript's is a required keyword 
			-- for the delimiting procedure to work properly
			set AppleScript's text item delimiters to {"/"}
			-- text 1 thru second last text item    
			set theParent to text 1 thru (text item -2) of itemx
			set AppleScript's text item delimiters to ""
		end if
		
		--log "itemx = " & itemx & " theParent = " & theParent & " thisItem = " & thisItem
		if thisItem is not "" then
			-- what does this do
			if (theParent ends with thisItem) then
				set end of filtered_list to itemx
			end if
		end if
	end repeat
	
end myfilter

(*
---https://www.macscripter.net/viewtopic.php?id=47017
on tableViewSelectionDidChange:aNotification
	set tableView to aNotification's object
	set selectedRowIdx to (tableView's selectedRow) as integer
	set rowData to arrayController's arrangedObjects's objectAtIndex:selectedRowIdx
	set theNibName to (rowData's nibName) as text
	
	if theNibName is not "" then
		set viewController to current application's class "MyViewController"'s alloc's initWithNibName:(rowData's nibName) bundle:(current application's NSBundle's mainBundle)
		viewController's loadView()
		set theView to viewController's view
		set theView's translatesAutoresizingMaskIntoConstraints to false
		set detailSubviews to (detailView's subviews) as list
		if detailSubviews is not {} then
			set oldLabel to item 1 of detailSubviews
			detailView's replaceSubview:oldLabel |with|:theView
		else
			detailView's addSubview:theView
		end if
		
		(* set view constraints *)
		set constraintLeading to theView's leadingAnchor's constraintEqualToAnchor:(detailView's leadingAnchor)
		set constraintTop to theView's topAnchor's constraintEqualToAnchor:(detailView's topAnchor)
		set constraintBottom to theView's bottomAnchor's constraintEqualToAnchor:(detailView's bottomAnchor)
		set constraintTrailing to theView's trailingAnchor's constraintEqualToAnchor:(detailView's trailingAnchor)
		constraintLeading's setActive:true
		constraintTop's setActive:true
		constraintBottom's setActive:true
		constraintTrailing's setActive:true
	end if
end tableViewSelectionDidChange:
*)


Model: MacBook Pro 2012
AppleScript: 2.7
Browser: Safari 13.0.1
Operating System: macOS 10.14