Improved Choose From List Dialog

Using the Choose From List command of Standard Additions, I came across 2 significant flaws. 1) The first is the impossibility of obtaining the index of an element selected by the user (NOTE: list contains duplicates too). And I want to continue the process, starting with the selected list item and further, without returning to the already processed ones. 2) The second problem concerns the impossibility of customizing and adding buttons.

I asked for help in post Get index of element, chozen in choose from list dialog and got a positive solution from Shane Stanley - to use his “Myriad Tables Lib”. It solves the above problems fine, so I was interested in how Shane does it. Unfortunately, this script library is run-only, so I was not able to look into the “little secrets” of this library. Fortunately, I found another script that was written by a Japanese Takaaki Naganoya (Macscripter user maro). I edited it a bit and provide it here. The script does not use third-party software, although it may be slightly slower than the library from Shane Stanley. So, the script:


use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |⌘| : a reference to current application
property aList : {3, "None", "Apple", 3, 4, "Chick", "Gyouza", "Snagi also", "Apple", "Peaches"}
property aMainMessage : "IMPROVED CHOOSE FROM LIST DIALOG"
property aSubMessage : "Please select the appropriate one from the following"
property theAlert : missing value
property theResult : 0
property returnCode : 0
property theDataSource : {}

my performSelectorOnMainThread:"createAlert" withObject:(missing value) waitUntilDone:true
my performSelectorOnMainThread:"displayAlert" withObject:(missing value) waitUntilDone:true
if theResult = (|⌘|'s NSAlertSecondButtonReturn) then error number -128
set aResult to my chooseItemByTableView()

on createAlert()
	set my theAlert to |⌘|'s NSAlert's alloc()'s init()
	my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true
end createAlert

on chooseItemByTableView()
	-- define the matrix size where you’ll put the radio buttons
	set aScrollWithTable to makeTableView(300, 150) of me
	-- set up alert
	tell theAlert
		its setMessageText:aMainMessage
		its setInformativeText:aSubMessage
		its addButtonWithTitle:"OK"
		its addButtonWithTitle:"Cancel"
		its setAccessoryView:aScrollWithTable
	end tell
	-- show alert in modal loop
	|⌘|'s NSRunningApplication's currentApplication()'s activateWithOptions:0
	my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
	if (my returnCode as number) = 1001 then error number -128
	return (aScrollWithTable's documentView's selectedRow()) + 1
end chooseItemByTableView

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

on makeTableView(aWidth as number, aHeight as number)
	set aOffset to 40
	
	set sourceList to {}
	repeat with i in aList
		set the end of sourceList to {dataItem:(contents of i)}
	end repeat
	
	set theDataSource to |⌘|'s NSMutableArray's alloc()'s init()
	theDataSource's addObjectsFromArray:sourceList
	
	set aScroll to |⌘|'s NSScrollView's alloc()'s initWithFrame:(|⌘|'s NSMakeRect(0, aOffset, aWidth, aHeight))
	set aView to |⌘|'s NSTableView's alloc()'s initWithFrame:(|⌘|'s NSMakeRect(0, aOffset, aWidth, aHeight))
	
	set aColumn to (|⌘|'s NSTableColumn's alloc()'s initWithIdentifier:"dataItem")
	(aColumn's setWidth:aWidth)
	(aColumn's headerCell()'s setStringValue:"dataItem")
	(aView's addTableColumn:aColumn)
	
	aView's setDelegate:me
	aView's setDataSource:me
	aView's reloadData()
	
	aScroll's setDocumentView:aView
	aView's enclosingScrollView()'s setHasVerticalScroller:true
	
	-- Select line
	set aIndexSet to |⌘|'s NSIndexSet's indexSetWithIndex:0
	aView's selectRowIndexes:aIndexSet byExtendingSelection:false
	
	-- Force scroll to top
	-- set maxHeight to aScroll’s documentView()’s |bounds|()’s |size|()’s height
	set aDBounds to aScroll's documentView()'s |bounds|()
	if class of aDBounds = list then
		-- macOS 10.13 or later
		set maxHeight to item 2 of item 1 of aDBounds
	else
		-- macOS 10.10….10.12
		set maxHeight to height of |size| of aDBounds
	end if
	
	set aPoint to |⌘|'s NSMakePoint(0.0, -40.0)
	aScroll's documentView()'s scrollPoint:aPoint
	
	return aScroll
end makeTableView

-- TableView Event Handlers
on numberOfRowsInTableView:aView
	return my theDataSource's |count|()
end numberOfRowsInTableView:

on tableView:aView objectValueForTableColumn:aColumn row:aRow
	set aRecord to (my theDataSource)'s objectAtIndex:(aRow as number)
	set aTitle to (aColumn's headerCell()'s title()) as string
	set aResult to (aRecord's valueForKey:aTitle)
	return aResult
end tableView:objectValueForTableColumn:row: