AppleScript Dialogs with Image Thumbnails

I have a script that searches for missing images in an InDesign document and updates each image if it is found. If it finds more than 1 image available for a missing image, it displays a dialog with the paths to each image so the user can choose which image they want to use.

Below is a code sample with the part that displays the dialog. Instead of displaying a dialog with several image paths that the user can choose from, I would like to display a dialog with thumbnails of each image with the image’s name beneath them. Is there a way to do this using Applescript Objective C or InDesign custom dialogs? If so, any help on how to do that would be greatly appreciated!

set example_image_number to "*317871*"
set example_image_name to "317871.jpg"
set example_images_folder to POSIX path of "/Volumes/NAS/Advertising Department/2_IMAGES - HIGH RES FOR PRINT/1_4c IMAGES/"

--Doing a search for the image number
set image_search to paragraphs of (do shell script "find " & quoted form of (POSIX path of example_images_folder) & " -name '" & example_image_number & "' -type f") -->searching 

--Displaying a list of found images to choose from if more than 1 image is found for that number
if length of image_search is greater than 1 then
	tell application "Adobe InDesign CC 2018"
		activate
		set prompt_name to "Please select the correct image to replace missing image " & example_image_name & " with:"
		set my_choice to (choose from list image_search with prompt prompt_name) as string
	end tell
end if

Yes it is possible, I’m showing complex dialogs, progress indicators, modal windows and other things using only AppleScriptObjC (without any NIB file). Here is how you create an window on the Fly properly, don’t worry about the script object because it will be released after the window closes. Which means it’s like an “stay-open application”.

use AppleScript version "2.5"
use framework "Foundation"

property NSWindowController : class "NSWindowController"
property NSWindow : class "NSWindow"

property windowController : missing value

set theWindow to createWindowWithRect(0, 0, 500, 500)
set windowController to createWindowController(theWindow)

windowController's |window|()'s |center|()

my performSelectorOnMainThread:"showWindowSelector:" withObject:me waitUntilDone:true
return

on createWindowWithRect(xMin, yMin, xLen, yLen)
	set windowSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
	set aWindow to NSWindow's alloc()'s initWithContentRect:windowSize styleMask:7 backing:2 defer:yes
	return aWindow
end createWindowWithRect

on createWindowController(aWindow)
	set wController to NSWindowController's alloc()'s initWithWindow:aWindow
end createWindowController

on showWindowSelector:sender
	windowController's |window|()'s makeKeyAndOrderFront:me
end showWindowSelector:

This is a good skeleton to start with and you can add subviews to theWindow to you liking.

Thank you!

When I look up how to add an image to a subview, I found the following code which does not play well with the Script Editor. (It keeps telling me UIImage is undefined).

UIImage* image = [[UIImage alloc] initWithContentsOfFile: path];
	UIImageView *imageView = [[UIImageView alloc] initWithImage: image];
[self.view addSubview: imageView];

Here’s something else I tried, which gives no errors but also doesn’t add the image or subview.

use AppleScript version "2.4"
use framework "Foundation"

property NSWindowController : class "NSWindowController"
property NSWindow : class "NSWindow"

property windowController : "Cooperate"

set theWindow to createWindowWithRect(0, 0, 500, 500)
set windowController to createWindowController(theWindow)

windowController's |window|()'s |center|()

my performSelectorOnMainThread:"showWindowSelector:" withObject:me waitUntilDone:true
return

on createWindowWithRect(xMin, yMin, xLen, yLen)
	set windowSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
	set aWindow to NSWindow's alloc()'s initWithContentRect:windowSize styleMask:7 backing:2 defer:yes
	return aWindow
end createWindowWithRect

on createWindowController(aWindow)
	set wController to NSWindowController's alloc()'s initWithWindow:aWindow
end createWindowController

on showWindowSelector:sender
	windowController's |window|()'s makeKeyAndOrderFront:me
	set mySubview to windowController's |window|()'s addSubview:me
	set myImage to mySubview()'s initWithImage:"/Volumes/NAS/Advertising Department/2_IMAGES - HIGH RES FOR PRINT/1_4c IMAGES/AG1113214(2_18)_s_4c.tif"
end showWindowSelector:

Hi DJ.

I’m just studying your script to try and understand how it works.

  1. Shouldn’t it also use the “AppKit” framework?
  2. All the methods seem to date from Mac OS 10.0 or 10.2, so maybe AppleScript version “2.4” would be OK.
  3. The NSWindowController is only used here to get references to the window — to which the script has a reference already. Presumably the controller would have other uses if the skeleton were expanded.
  4. Is there any essential difference between this:
on showWindowSelector:sender
	windowController's |window|()'s makeKeyAndOrderFront:sender -- me
end showWindowSelector:

… and this?:

on showWindowSelector:sender
	windowController's showWindow:sender
end showWindowSelector:

It’s indeed better to write it explicitly, I agree. The reason I don’t is that every application that’s capable of running AppleScriptObjC code has AppKit already loaded. In order for an process to become an application it has to load in instance of NSApplication which is part of the AppKit framework.

The context was more that’s it’s better to use the latest version of AppleScript rather than it won’t work in older AppleScript versions.

That’s correct, this specific example could do without an window controller because it doesn’t require specific needs.

In this case not really because there is only one window and that windows becomes inherently key and front most. As with my previous answer, in this specific example showWindow: works the same but when you expand the script it can behave differently. When expanding the script working with multiple windows, sheets and or panels or change the default behavior of this window, showWindow: can behave differently from makeKeyAndOrderFront:.

UIImage is for iOS, not macOS. You need NSImage. This should give you something to go on:

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"

property NSWindow : a reference to current application's NSWindow
property NSImage : a reference to current application's NSImage
property NSImageView : a reference to current application's NSImageView
property NSWindowStyleMaskTitled : a reference to 1
property NSWindowStyleMaskClosable : a reference to 2


set theWindow to createWindowWithRect(0, 0, 500, 500)
my performSelectorOnMainThread:"showTheWindow:" withObject:theWindow waitUntilDone:true

on createWindowWithRect(xMin, yMin, xLen, yLen)
	set windowSize to {{xMin, yMin}, {xLen, yLen}}
	set winStyle to NSWindowStyleMaskTitled + NSWindowStyleMaskClosable
	set aWindow to NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:2 defer:yes
	return aWindow
end createWindowWithRect

on showTheWindow:theWindow
	set theImage to NSImage's alloc()'s initWithContentsOfFile:"/users/shane/Desktop/IMG_0194.jpg"
	set theImageView to NSImageView's alloc()'s initWithFrame:{{50, 50}, {400, 400}}
	theWindow's contentView()'s addSubview:theImageView
	theImageView's setImage:theImage
	theWindow's makeKeyAndOrderFront:me
end showTheWindow:

Edited to cope with changing enum names

There’s more to it, though. The use framework statement also tells AppleScript to load the framework’s .bridgesupport file, which in turn is required for various bits of terminology, as well as some methods. I actually asked one of the engineers about it, and he was quite emphatic that it should be included.

Didn’t knew that. Always assumed that the very slow startup of AppleScriptObjC was mostly caused by loading bridge supports files of already loaded frameworks (or application frameworks).

Thanks

DJ, Shane. Thanks for the replies. :slight_smile:

Sweet! :smiley: I have it displaying a window with four images from an AppleScript list now. I need to do some more research on adding text and buttons and passing the chosen button back to AppleScript. How did you learn Objective C?

-------------------------------------------------------------------------------------------------------------
--ADDING THE MISSING IMAGES TO A LIST
-------------------------------------------------------------------------------------------------------------

--Adding the images to the list
tell application "Finder"
	set image_option1 to POSIX path of "Volumes:NAS:Advertising Department:2_IMAGES - HIGH RES FOR PRINT:1_4c IMAGES:CLK - High Res:CLK_ BY BLAIN NUMBERS - High Res:CLK_0 - CLK_5 - High Res:CLK1029172_VFJeanswear(5_18)A_s_4c.tif" as string
	set image_option2 to POSIX path of "Volumes:NAS:Advertising Department:2_IMAGES - HIGH RES FOR PRINT:1_4c IMAGES:CLK - High Res:CLK_ BY BLAIN NUMBERS - High Res:CLK_0 - CLK_5 - High Res:CLK1029172_VFJeanswear(5_18)B_s_4c.tif" as string
	set image_option3 to POSIX path of "Volumes:NAS:Advertising Department:2_IMAGES - HIGH RES FOR PRINT:1_4c IMAGES:CLK - High Res:CLK_ BY BLAIN NUMBERS - High Res:CLK_0 - CLK_5 - High Res:CLK1029172_VFJeanswear(5_18)C_s_4c.tif" as string
	set image_option4 to POSIX path of "Volumes:NAS:Advertising Department:2_IMAGES - HIGH RES FOR PRINT:1_4c IMAGES:CLK - High Res:CLK_ BY BLAIN NUMBERS - High Res:CLK_0 - CLK_5 - High Res:CLK1029172_VFJeanswear(5_18)D_s_4c.tif" as string
	set image_list to {image_option1, image_option2, image_option3, image_option4}
	
	--Getting the number of items in the list
	set image_count to count image_list
	
	--Figuring out the height of the display window based on the number of images in the list
	if image_count is less than or equal to 4 then
		set image_rows to 1
	else
		set image_rows to 2
	end if
	set screen_height to (400 * image_rows) as number
	
end tell


-------------------------------------------------------------------------------------------------------------
--CREATING THE UI WINDOW
-------------------------------------------------------------------------------------------------------------

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"

property NSWindow : a reference to current application's NSWindow
property NSImage : a reference to current application's NSImage
property NSImageView : a reference to current application's NSImageView
property NSWindowStyleMaskTitled : a reference to 1
property NSWindowStyleMaskClosable : a reference to 2

set theWindow to createWindowWithRect(0, 0, 1000, screen_height)
my performSelectorOnMainThread:"showTheWindow:" withObject:theWindow waitUntilDone:true

--Creating the window 
on createWindowWithRect(xMin, yMin, xLen, yLen)
	set windowSize to {{xMin, yMin}, {xLen, yLen}}
	set winStyle to NSWindowStyleMaskTitled + NSWindowStyleMaskClosable
	set aWindow to NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:2 defer:yes
	return aWindow
end createWindowWithRect

--**********--**********--**********--**********--**********--

--> add the heading "MULTIPLE IMAGE(S) FOUND. Which image would you like to use?" to the UI

--**********--**********--**********--**********--**********--

--Adding the images to the UI Window
set x1 to 200
repeat with image_option in image_list
	tell application "Finder" to set image_name to name of (POSIX file (image_option as string) as alias) -->getting the image name for the label
	log image_name
	set theImage to (NSImage's alloc()'s initWithContentsOfFile:image_option)
	set theImageView to (NSImageView's alloc()'s initWithFrame:{{25, 50}, {x1, 300}})
	(theWindow's contentView()'s addSubview:theImageView)
	(theImageView's setImage:theImage)
	
	--**********--**********--**********--**********--**********--
	
	--> add image name beneath the image as a label to the UI
	
	--**********--**********--**********--**********--**********--
	
	
	--**********--**********--**********--**********--**********--
	
	--> add buttons to the UI for the user to choose which image they want to use
	
	--**********--**********--**********--**********--**********--
	
	set x1 to (x1 + 450)
end repeat


--Showing the window
on showTheWindow:theWindow
	(theWindow's makeKeyAndOrderFront:me)
end showTheWindow:



--**********--**********--**********--**********--**********--

--> Pass the selected image back to AppleScript as a variable

--**********--**********--**********--**********--**********--

If you download my Dialog Toolkit Plus script library here:

https://www.macosxautomation.com/applescript/apps/Script_Libs.html

it should tell you most of what you need to know.

Books and sample code.

You can define the handler that’s need to be called when an button is clicked by using setAction: (see createButton handler below).

use AppleScript version "2.4"
use framework "AppKit"
use framework "Foundation"
use scripting additions -- to show a dialog

property NSWindowController : class "NSWindowController"
property NSWindow : class "NSWindow"
property NSButton : class "NSButton"

property windowController : missing value

set theWindow to createWindowWithRect(0, 0, 500, 500)
set windowController to createWindowController(theWindow)

set theButton to createButton("Click me", 20, 20, 100, 24, "buttonAction:")
theWindow's contentView()'s addSubview:theButton

windowController's |window|()'s |center|()

my performSelectorOnMainThread:"showWindowSelector:" withObject:me waitUntilDone:true
return

on buttonAction:sender
	display dialog "I'm clicked"
end buttonAction:

on createButton(title, xMin, yMin, xLen, yLen, selector)
	set buttonSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
	set theButton to NSButton's alloc()'s initWithFrame:buttonSize
	theButton's setTitle:title
	theButton's setBezelStyle:(current application's NSRoundedBezelStyle)
	theButton's setAction:selector
	theButton's setTarget:me
	return theButton
end createButton

on createWindowWithRect(xMin, yMin, xLen, yLen)
	set windowSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
	set winStyle to (current application's NSWindowStyleMaskTitled) + (get current application's NSWindowStyleMaskClosable)
	set aWindow to NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:2 defer:yes
	return aWindow
end createWindowWithRect

on createWindowController(aWindow)
	set wController to NSWindowController's alloc()'s initWithWindow:aWindow
end createWindowController

on showWindowSelector:sender
	windowController's |window|()'s makeKeyAndOrderFront:me
end showWindowSelector:

I already knew the base language of Objective-C (That is C) which made learning Objective-C quite easy but I have to give credits to Aaron Hillegass of The Big Nerd Ranch who beautifully explains the programming language. It was back in Panther days when I learned it, so my style of coding Objective-C is not the most modern (lazy) one. While most books explains how to use frameworks and it’s classes, which is irrelevant for learning of Objective-C, Aaron explains the design patterns behind it.

I strongly second DJ’s recommendation of Hillegass. I don’t know how up-to-date it is any more, but his ‘Cocoa Programming for Mac OS X’ was my bible. And if you have no C, the section in Matt Neuburg’s iOS book called something like ‘Just Enough C’ is worth tracking down.