Edit,copy,delete, insert or select multiple items from a list

This stand alone library is dependent of the Script Cache Storage Library

(*
Version two: multiple selections allowed, one command and one item, to speed up things.
Copyright 2015 ©  McUsr 
*)

use AppleScript version "2.3"
use scripting additions
use cacheStorage : script "Script Cache Storage"

property insertCommandChoice : "--- New/Insert"
property copyCommandChoice : "--- Copy/Duplicate"
property editCommandChoice : "--- Edit/Replace"
property deleteCommandChoice : "--- Delete/Remove"
property menuChoices : {insertCommandChoice, copyCommandChoice, editCommandChoice, deleteCommandChoice}

-- DON'T CHANGE THE PROPERTIES BELOW, THEY CONTROL THE INTERAL LOGIC!

-- property driverList : {"Ananas", "Grapes", "Strawberries", "BlueBerries"}

on __indexOfItem(theItem, theList) -- credit to Emmanuel Levy but I modified it with the considering case statements
	set text item delimiters to return
	set theList to return & theList & return
	set text item delimiters to {""}
	try
		-1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
	on error
		0
	end try
end __indexOfItem


(*
set res to listHandler("TestCache.scpt", "driver", "Fruit", "Snack", true)
log res
*)
on listHandler(theCacheName, fsysName, itemDescription, taskDescription, multiple)
	-- Initialization: We set up the menu name, and read in data from the stored scriptcache, if any!
	set scriptTitle to fsysName & ": " & taskDescription
	
	set theCache to loadScript of cacheStorage from theCacheName
	
	set PORTIFOLIO to theCache's itemslist
	
	-- * set PORTIFOLIO to my driverList
	-- Initialization 
	-- need to make the cachename first!
	
	-- Do not forget that you must grab search expression from find window too.
	set currentState to "anything"
	
	set listHasChanged to false -- Determines whether we should save the list or not
	if multiple then
		set startOfListDialogPrompt to "Choose " & itemDescription & "(s) to maintain, or  use  for a "
	else
		set startOfListDialogPrompt to "Choose a " & itemDescription & " to maintain, or  use  for a "
	end if
	repeat
		-- preparation of the Menu
		if currentState is "anything" then
			set menuList to menuChoices & PORTIFOLIO
			
			set menuLevel to ": Main "
			set listDialogPrompt to startOfListDialogPrompt & taskDescription & "."
		else if currentState is in {"pickItemToEdit", "pickItemToDelete", "pickItemToDuplicate"} then -- TODO
			if PORTIFOLIO is not {} then
				set menuList to PORTIFOLIO -- List items 
				if currentState is "pickItemToEdit" then
					set menuLevel to ": Edit "
					set listDialogPrompt to "Choose a " & itemDescription & " for a " & taskDescription & " that you want to Edit."
				else if currentState is "pickItemToDelete" then
					set menuLevel to ": Delete "
					set listDialogPrompt to "Choose a " & itemDescription & " for a  " & taskDescription & " that you want to Delete."
				else if currentState is "pickItemToDuplicate" then
					set menuLevel to ": Duplicate "
					set listDialogPrompt to "Choose a " & itemDescription & " for a " & taskDescription & " that you want to Duplicate."
				end if
			else
				
				tell application (path to frontmost application as text)
					display dialog "There are no Search Criteria that can be Edited/Deleted/Duplicated, please Insert one." with title scriptTitle buttons {"Stop"} default button 1 with icon stop
				end tell
				set menuList to menuChoices & PORTIFOLIO -- unnecessary
				set currentState to "anything"
			end if
		end if
		-- Display of the "Menu" 
		if currentState is not "shallInsert" then
			tell application (path to frontmost application as text)
				
				set chosenItems to choose from list menuList default items (item 1 of menuList) with prompt listDialogPrompt with title (scriptTitle & menuLevel) with multiple selections allowed
			end tell
			if chosenItems is not false then
				if currentState is "pickItemToEdit" then
					set currentState to "shallEdit"
				else if currentState is "pickItemToDelete" then
					set currentState to "shallDelete"
				else if currentState is "pickItemToDuplicate" then
					set currentState to "shallDuplicate"
				end if
			end if
		end if
		-- processing of menu choices, including shallInsert.
		if currentState is "anything" then -- Main Level 
			
			if chosenItems is false then
				exit repeat
			else
				set itemCount to count chosenItems
			end if
			if itemCount = 0 then
				tell application (path to frontmost application as text)
					display alert fsysName & ":
You have chosen no elements. I quit"
				end tell
				set chosenItems to false
				exit repeat
			else if itemCount = 1 then
				-- We'll actually do something 
				set singleChoice to item 1 of chosenItems
				if singleChoice is in menuChoices then
					-- log "entered single command choice"
					--					log "Administrative task"
					if singleChoice is my editCommandChoice then
						set currentState to "pickItemToEdit"
					else if singleChoice is my insertCommandChoice then
						set currentState to "shallInsert"
					else if singleChoice is my deleteCommandChoice then
						set currentState to "pickItemToDelete"
					else if singleChoice is my copyCommandChoice then
						set currentState to "pickItemToDuplicate"
					end if
				else
					if currentState = "anything" then
						set currentState to "elementSelected"
						-- We are returning an element to the handler/script that invoked us
						exit repeat
					else if currentState = "pickItemToEdit" then
						set currentState to "shallEdit"
					else if currentState = "pickItemToDelete" then
						set currentState to "shallDelete"
					else if currentState = "pickItemToDuplicate" then
						set currentState to "shallDuplicate"
					end if
				end if
			else if itemCount = 2 then
				(*
					 Business rules:
					 insert can not be one of the actions.
					 Not both items can be actions.
				*)
				if item 1 of chosenItems is my insertCommandChoice then
					tell application (path to frontmost application as text)
						display alert fsysName & ":
You can't both choose an item, and choose to insert."
					end tell
					set currentState to "anything"
				else if (item 1 of chosenItems is in menuChoices) and (item 2 of chosenItems is not in menuChoices) then
					set commandChoice to item 1 of chosenItems
					set chosenItems to rest of chosenItems
					if commandChoice is my editCommandChoice then
						set currentState to "shallEdit"
					else if commandChoice is my deleteCommandChoice then
						set currentState to "shallDelete"
					else if commandChoice is my copyCommandChoice then
						set currentState to "shallDuplicate"
					end if
				else if item 1 of chosenItems is not in menuChoices then
					-- 2 items selected	
					if multiple then
						set currentState to "elementSelected"
						-- We are returning elements to the handler/script that invoked us
						exit repeat
					else
						tell application (path to frontmost application as text)
							display alert fsysName & ":
Selecting multiple items is not allowed "
						end tell
						set currentState to "anything"
					end if
					
				else -- 2 commands!
					tell application (path to frontmost application as text)
						display alert fsysName & ":
You can't choose two commands at once."
					end tell
					set currentState to "anything"
				end if
			else -- temCount > 2 
				(*
					Business rules:
					No item can be an action
				*)
				if item 1 of chosenItems is not in menuChoices then
					if multiple then
						set currentState to "elementSelected"
						-- We are returning elements to the handler/script that invoked us
						exit repeat
					else
						tell application (path to frontmost application as text)
							display alert fsysName & ":
Selecting multiple items is not allowed "
						end tell
						set currentState to "anything"
					end if
					
				else
					tell application (path to frontmost application as text)
						display alert fsysName & ":
You can't choose to do a command on more than one item at a time."
					end tell
					set currentState to "anything"
				end if
			end if
		end if
		-- out of the loop
		if currentState is in {"shallEdit", "shallDelete", "shallDuplicate"} then
			
			if PORTIFOLIO is not {} and chosenItems is not false then -- Sub Level, (Picking Item.)

				set pickedItemNo to __indexOfItem(item 1 of chosenItems, PORTIFOLIO)
				if currentState is "shallEdit" then
					try
						tell application (path to frontmost application as text)
							
							set newText to text returned of (display dialog "Edit the " & itemDescription & ":" default answer item pickedItemNo of PORTIFOLIO with title scriptTitle & " : Edit " with icon caution)
						end tell
					on error
						set currentState to "anything"
					end try
					
					if currentState is "shallEdit" then
						set item pickedItemNo of PORTIFOLIO to newText
						set currentState to "anything"
						set listHasChanged to true
					end if
					
				else if currentState is "shallDelete" then
					try
						tell application (path to frontmost application as text)
							display dialog "Do you really want to delete the " & itemDescription & ":
" & item pickedItemNo of PORTIFOLIO & "?" with title scriptTitle & " : Delete" with icon caution
						end tell
					on error
						set currentState to "anything"
					end try
					
					if currentState is "shallDelete" then
						if pickedItemNo = 1 then
							set PORTIFOLIO to rest of PORTIFOLIO
						else if pickedItemNo = (count PORTIFOLIO) then
							set PORTIFOLIO to items 1 thru -2 of PORTIFOLIO
						else
							set PORTIFOLIO to (items 1 thru (pickedItemNo - 1) of PORTIFOLIO) & (items (pickedItemNo + 1) thru -1 of PORTIFOLIO)
						end if
						set listHasChanged to true
						set currentState to "anything"
					end if
					
				else if currentState is "shallDuplicate" then
					set theDuplicate to item pickedItemNo of PORTIFOLIO
					if pickedItemNo = 1 then
						set PORTIFOLIO to {item 1 of PORTIFOLIO} & {theDuplicate} & rest of PORTIFOLIO
					else if pickedItemNo = (count PORTIFOLIO) then
						set PORTIFOLIO to PORTIFOLIO & theDuplicate
					else
						set PORTIFOLIO to items 1 thru pickedItemNo of PORTIFOLIO & theDuplicate & items (pickedItemNo + 1) thru -1 of PORTIFOLIO
					end if
					set listHasChanged to true
					set currentState to "anything"
					
				end if
			else
				set currentState to "anything"
			end if
		else if currentState is "shallInsert" then
			-- No preprocessing/Listchanging here  so we don't have think of empty  PORTIFOLIO or if 
			-- chosenItems is false!
			try
				tell application (path to frontmost application as text)
					set newText to text returned of (display dialog "Enter the " & itemDescription & " you wish to insert :" default answer "" with title scriptTitle & " : Insert " with icon caution)
				end tell
			on error
				set currentState to "anything"
			end try
			
			if currentState is "shallInsert" then
				if newText is not "" then
					set end of PORTIFOLIO to newText
					set listHasChanged to true
				end if
				set currentState to "anything"
			end if
		end if
		-- PostProcessing, we have to save the list as it is changed.
		
		if listHasChanged then
			
			set theCache's itemslist to PORTIFOLIO
			set success to storeScript of cacheStorage from theCacheName against theCache
			if not success then
				tell application (path to frontmost application as text)
					display alert fsysName & ":
I couldn't store the current script object, aborts"
				end tell
				return null
			end if
			set listHasChanged to false
		end if
		
	end repeat
	
	return chosenItems
end listHandler

A little driver script:

use AppleScript version "2.3"
use scripting additions
use lm : script "ListMaintainer"

set theCacheName to "Metatags.scpt"
log theCacheName

set returnedKeywords to lm's listHandler(theCacheName, "Metatags", "Keyword", "Keyword Meta Tag", true)
if returnedKeywords is null then
	tell application (path to frontmost application as text)
		
		display alert "Problems during saving cache to disk. I  quit."
	end tell

	return
else if returnedKeywords is false then
	tell application (path to frontmost application as text)
		
		display alert "No tags selected, Be sure to update the Meta tags later."
	end tell
	set returnedKeywords to ""
else
	-- returns a list of comma separated values
	-- this handler is really intended for single values
	set astid to my text item delimiters
	set my text item delimiters to ", "
	
	set returnedKeywords to items of returnedKeywords as text
	set my text item delimiters to astid
	
	log "" & class of returnedKeywords & " : " & returnedKeywords
end if
log returnedKeywords

I hope you like it, and that you report any bugs, that I may not have come across, it has been working flawlessly for me however for some time.

Edit:

  • I made an error message for the driver script less confusing.
  • Some formatting errors in the doc are corrected.
  • Removed an outdated paragraph in the doc.

Hello.

I did introduce a bug, when I made it take a command, and an item to perform upon, that resulted in a runtime error when you cancel the choose from list dialog, this is corrected.

I have also made the handler to exit with an empty list and if you choose nothing, then that is taken as a decline of the choose from list, and that situation returns false as well.

I’m sorry for any inconvenience.

Hello.

Here is a script you can use to modify to your own needs, it just really illustrates the usage of ListMaintainer:

I use this script, so that I have certain searchwords or regular expressions stored exclusively for that particular document tree, this is great if you don’t work on a project for a while, and then have to go back again.

-- Script SourceTree: A script for storing regular expressions while working on a project.
use AppleScript version "2.3"
use scripting additions
use lm : script "ListMaintainer"
property mainBranchOfCacheName : "C-"
property cachePath : (path to application support folder from user domain as text) & "Script Cache Storage:"
-- Editing starts here:
property systemName : "SourceTree"
property SearchItems : missing value

to multiFileSearchWindow by SearchPattern from SearchItems
	tell application "TextWrangler"
		set SearchOptions to {search mode:grep, case sensitive:true, match words:false, showing results:true, returning results:false}
		find SearchPattern searching in SearchItems options SearchOptions
	end tell
end multiFileSearchWindow

on run
	tell application "TextWrangler"
		activate
		if my SearchItems is missing value then
			set my SearchItems to choose folder with prompt "Choose a search folder:"
		end if
	end tell
	
	set theCacheName to (my cachePath & my mainBranchOfCacheName & systemName & ".scpt") as text
	set searchExpr to lm's listHandler(theCacheName, systemName, "Search Criteria", "Multi-File Search", false)
	
	if searchExpr is not false then
		multiFileSearchWindow by searchExpr from SearchItems
	end if
end run