Friday, December 15, 2017
  • Index
  •  » Code Exchange
  •  » Edit,copy,delete, insert or select multiple items from a list

#1 2015-02-19 12:11:31 pm

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

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

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

Description of the ListMaintainer Library
ListMaintainer is an AS 2.3 Library, that has a handler that maintains lists with text elements, or lets the user chose an element for use elsewhere in the script, the list is stored onto and read from the disk, so it persists between edits of scripts for instance. The great thing with this library, is that in addition to letting you insert, duplicate, edit and delete list items, it lets you choose an action, and an element of the list to perform on, in one step should you wish to , saving a considerable number of steps while maintaining a list.

How it works

Items are used here, as user entered items in contrast to the edit commands.
If the user selects more than two items, that are both “items” then we shall return the items unless the listHandler() is called with the parameter multiple set to false

*if the user selects two actions, or one action, and two elements then that is an error.

*if the user selects two items, one action, and one item, then we perform the action on that item directly.

*If the user selects just one action, he/she will be prompted for selecting the element to perform the action on (copy,edit,delete) this doesn't work for insert command, since it doesn't operate on an element to begin with.

*If the user select one or more items, then those items are returned, if the handler is called with the parameter multiple is set to true.

The handler returns parameters, much like choose from list but there is on important addition: the parameter null is returned if an error occured while writing a script object.

Actions that are supported:
The listHandler lets you perform actions on the list like “New/Insert” list item, “Copy/Duplicate” list item, “Edit/Replace” list item, or “Delete/Remove” list item.

You can of course also select  more than one element of the list to return if the listHandler() has been called with the parameter multiple set to true, or one element if the the value of multiple is false, (the short cut, for choosing one action, and one element still works). The element(s) will be returned to your calling script.


Internals

Overview
This is a two level modal menu system, that puts actions on list elements, and the list elements it maintains in the same list. It uses persistent storage (script “cacheStorage”), and the list is stored as soon as you have changed it. You actually retrieve/create a new list by its filename in the ~/Library/Caches folder.

The listHandler() works by iterating over a loop until we are done, going through branches in accordance with the current state evoked by the user's actions so we always show the correct “menu” for the current state/context.


Details
At the main level, we can perform all tasks, on the next level, we'll perform a task on the items, according to the nature of the task. Or we skip that step if we select an action, and an element. Have we chosen the “Insert/New” action, then we can enter an item, if we have chosen “Delete/Remove”, then we delete, and so on. When we have chosen an action to do on the list we return to the main level, where the updated element list is shown. If we choose (an) element(s) ,then we exit the handler and return it to the handler/script that called us. If we declined choosing anything, then false is returned, if an error occured during writing the script object, then null is returned.

If we exit when the main menu is shown, then we return from our handler with a value of false. The convention/idiom we use, is the same as for Standard Additons choose from list: if nothing is selected, we return false, if something is returned, it is returned within a list.

The storage library, contains a handler that lets you insert an item to your list from other scripts, I use that to snatch regular expressions from TextWrangler for instance.

Usage Notes

I use the ListMaintainer's listHandler() from scripts residing in various script menus therefore all feedback through choose from list, and other dialogs are shown in the context of the current application. This also implicates that it can become confusing if you use this handler from a User Agent. This also dictates, that if you want the any calling script to execute in the context of an app, (when you aren't calling the script for the applications local script menu, then that application should be activated before you fire up the ListMaintainer's handler().

I have tried to make the code execute reasonably fast, and yet be readable, I hope you agree.


Handlers

    •    on listHandler(theCacheName, fsysName, itemDescription, taskDescription)
   
    •    theCacheName: the hfsPath to the cache (stored script object) if you want to store the script object
        somewhere else than in the centralized location specified in the Script Cache Storage library.
    •    fsysName: The “system name” your own system name of sorts, if you are using the same cache for a set of         
        scripts, so it is easily identified.
    •    itemDescription: What each list item is. “Search Expression” is an example.
    •    taskDescription: What you are supposed to do with a list item, “Multi-File Search” is an example.
    •    Returns either a list with one item, or false in spirit with choose from list.
Requirements

    •    Be installed in the Script Libraries Folder.
    •    Use the AS2.3 Library convention.
    •    The list inside the script object must contain text items.
    •    It depends on the Script Cache Library that you can find here.


Applescript:

(*
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:

Applescript:

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.

Last edited by McUsrII (2015-02-21 02:42:40 pm)

Offline

 

#2 2015-02-21 02:46:21 pm

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

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

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.

Offline

 

#3 2015-02-22 02:24:37 pm

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

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

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.

Applescript:

-- 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

Offline

 
  • Index
  •  » Code Exchange
  •  » Edit,copy,delete, insert or select multiple items from a list

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)