Hello.
EditThis is work in progress, the code below which is a demo should work properly!
Grab it and play with it, and please tell me what you think. The documentation is a bit out of sync with the
result. I’ll post below when I am finished with the documentation..
If you have comments, please send me messages or email me; I intend to delete the whole thread when I’m finished, and post it as a “clean” post here at code exchange when I’m finished.
Abstract
smartList enhances the capability of your choose from list dialogs behind the scenes.
[b]This script object helps you to:[color=green]
¢Lookup a value in any corresponding table based on the original value or position.
¢Rearrange the elements in the displayed list where this is feasible, and keep the relations to other tables intact.
¢Keeping relations between lists intact when items are inserted or deleted at random positions.
[/color][/b]
Introduction to smartList
What I have tried to here is to write a small but powerfull list manager tool, implemented as a script object especially designed with the choose from list dialog in mind though it has other usages too.
This is for the dialogs for choosing items where you are most likely to select the same item, next time or have some other kind of heuristic and where it is gainful to have the list reflect the chronological order of the items that where chosen, or have the list rearranged in some other way. You can f.ex have your last chosen item marked as default. (optional and very easy anyway).
This is the way I generally prefer to have “list dialogs” react upon choices. Especially those I use infrequently where the dialog retains a run history of the script, which I find useful.
You can also use it to have the last item you chose pop up in some default position where it is standing by so that it can be selected upon the next invocation. In addition, you could have the last chosen element(s) show up in or around the middle position of the choose from list dialog. If that is what suits your needs.
This script object was made to make it easier to use lists in combination with choose from list dialogs in your scripts. This has often been a hindrance to me as some scripts I have wanted to make in say five minute becomes more daunting when I have had to consider the list handling in addition to the main tasks of the script. Tasks that were really simple in it’s nature became undoable within time contstraints by this.
Facts
The main job which the smartList lets you factor out, and implement in your script as prebuilt functionality is:
¢Keeping relations between two or more 1 to 1 related lists where corresponding elements
is in the exact same position: [b]whether elements are repositioned, the lists grow or shrink[/b].
¢ Moving a single or dynamic sized collection of list elements to a new position in a list, and
synchronize the related lists to keep the relations intact.
Specifics
Capability provided for your script:
¢ You can rearrange a displayed list, without regard to its relations with other lists:
either chronologically ascending or descending or by some other means.
¢ It can add either descending or ascending order of your users choices, which can make
choosing from a list more intuitive given the task at hand, dependent upon the likelihood
of doing a same next choice. It can also be used to provide a command history.
¢ There is also a centering mode, which I personally like when there are more elements in
the list, than visible on the screen, so that it is equally long way to both ends of the lists,
making the traversal of the list seem smaller.
Constraints and assumptions about your lists
.¢ Your list needs to consist of unique elements only. This script object will not reflect well
upon duplicate elements in a list and will produce an improper result.
¢ The lists aren't lexically sorted or must retain a lexically sorted order.
¢ If you use it to maintain relations among lists, then the relations must be consistent.
Meaning: [b]the same number of elements must be in each list, that's your responsibility.[/b]
The handler [b]updateList[/b] takes care of retaining the order of the elements in the lists
so that the relations aren't broken. [b]You must supply the extra necessary call per list to
keep everything in sync[/b].
¢ If your lists grow or shrink, then you must update the [b]listProperty[/b] object to reflect
the changes. [b]This must be done on a per deletion or insertion of a single element![/b].
This is done with the [b]updateListProperties[/b] handler. [b]It is your responsibility to
check that the list is not empty[/b] And you must of course [b]use the list you last selected
something from in order to update it properly[/b]!
¢ If you are to change your positioning scheme (absolute, relative or centering, then you
[b]must create a new listProperty object[/b]. What ever was selected from before is lost.
It is up to you to make calls to make the last selection reappear, which is easily done with
the [b]updateList[/b] handler.
¢ You must use the included [b]getDefaults[/b] handler for getting default items from your list
to feed the [b]choose from list command[/b] with.
Installation and usage
Copy the script object into your code and peek at the examples. Next, copy in the one or two handler calls or boilerplate codes into your original script then adjust the parameter names, and off you go. it is totally transparent to your own logic once it is installed, it manages itself.
That’s all.
Example walkthrough:
I have an unsorted list of items, and I use to have element nr 1 of the list as the default item, since that gives me a chronological list over what I have done:
My list contains in the following order: Apples Pears Bananas
They show up like this:
Apples (displayed as the default item)
Pears
Bananas
- I choose Bananas from the dialog; the list shows up like this on the next invocation:
Bananas (Is displayed as the default item)
Apples
Pears
This functionality was provided by this code in the script:
set fruitProperties to smartList's scoMakeListPropertyObject(1, fruitList)
set chosenFruit to choose from list fruitList default items smartList's getDefaults(false, true, fruitProperties) with multiple selections allowed and empty selection allowed
smartList's updateList( chosenFruit, fruitList, fruitProperties, true)
** – The return value chosenFruit is tell that this the isOrigList in case corresponding lists also need update.
-- this is done with identical calls, but with some differences in the supplied parameters.
This code has proven itself to work whether a choice consists of single or multiple items (choices).
Summary:
All you have to do, is to copy the script object into your code:
Create a property object for your list(s) via a handler call which takes care of everything.
Then you must add the handler getDefaults from smartList to your choose dialog if you wish to leverage that handlers capabilites to allways return the right things.
At last, after you have made your choice you must add a call to the handler updateList. That’s all there is to it. After you have done so, you can forget all about it. There is no hindrance or rework of your existing source or logic if this implementation level is suitable for your needs. Everything is taken care of behind the scenes. If you are having relations between lists, which you must retain, ( keep corresponding elements in the same positions) then you have to add some more invocations of the previous handler call and change some parameters.
Are there any constraints or assumptions I should know about?
First of all: This doesn’t work well with sorted lists. If your list isn’t sorted, then this will work very well for you. For a solution for sorted lists, you would only retain the last chosen elements as the default items of the next run.
See “Sorted Lists” at the end of the post.
What did you say my responsibilities were?
Example usage 1) Don’ let this frighten you, but get the script object from below and play with it.
…
…
If you want more functionality as your script or app is dependent upon a relation by position with items in another list, then you just need to call the same handler and provide it with the result of the first hander call, in order to make the relation intact. The same goes for a third list and so on.
Example usage 2 “ A full demo retaining relations between two lists.
-- Though Nigel Garvey and Emmanuel Levy has unknowingly contributed to this by providing handlers.
-- The Idea and implementation and any faults is totally mine. © McUsr 2010 and put in the Public Domain.
-- The usually guarrantees about nothing what so ever applies, use it at your own risk.
-- The code is however carefully tested, as the demo below will prove. Read the documentation. It is all
-- hidden somewhere in there.
-- If you want to use this code for some commercial project, let me know about it. -And we will have a talk.
” You are not allowed to post this code elsewhere, but may of course refer to the post at macscripter.net.
” macscripter.net/edit.php?id=130214
(*
TERMS OF USE.
This applies only to posting code, as long as you don't post it, you are welcome to do
whatever you want to do with it without any further permission.
Except for the following: Selling the code as is, or removing copyright statmentents and the embedded link in the code (without the http:// part) from the code.
You must also state what you eventually have done with the original source. This obviously doesn't matter if you distribure AppleScript as read only. I do not require you to embed any properties helding copyright notice for the code.
Credit for having contributed to your product would in all cases be nice!
If you use this code as part of script of yours you are of course welcome to post that code with my code in it here at macscripter.net. If you then wish to post your code elsewhere after having uploaded it to MacScripter.net please email me and ask for permission.
The ideal situation is however that you then refer to your code by a link to MacScripter.net
The sole reason for this, is that it is so much better for all of us to have a centralized codebase which are updated, than having to roam the net to find any usable snippets. Which some of us probabaly originated in the first hand.
I'm picky about this. If I find you to have published parts of my code on any other site without previous permission, I'll do what I can to have the site remove the code, ban you, and sue you under the jurisdiction of AGDER LAGMANNSRETT of Norway. Those are the terms you silently agree too by using this code.
The above paragraphs are also valid if you violate any of my terms.
If you use this or have advantage of this code in a professional setting, where professional setting means that you use this code to earn money by keeping yourself more productive. Or if you as an employee share the resulting script with other coworkers, enhancing the productivity of your company, then a modest donation to MacScripter.net would be appreciated.
*)
-- variables which just is for the demo.
set scriptTitle to "smartListDemo"
set iterNumber to 0
set theTitle to "Choose Items from a Static List"
set origTheThemesList to {"1 System Properties", "2 Basic Language Lore", "3 File I/O", "4 Dates", "5 Advanced Language Lore", "6 Text Manipulation", "7 Application - Script Models"}
set origRelatedLIst to {"chapter 1", "chapter 2", "chapter 3", "chapter 4", "chapter 5", "chapter 6", "chapter 7"}
copy origTheThemesList to theThemesList
copy origRelatedLIst to relatedLIst
set defaultPos to 1 -- This is an important variable, holding the default position or positioning method.
set scoThemesListProperties to smartList's scoMakeListPropertyObject(defaultPos, theThemesList)
set listChanged to false
-- listChanged is a variable I use to keep track of the changed status of the list.
-- If the list is changed, then we must update its properties.
-- You would want a variable like this signal that the list properties must be updated.
repeat
set iterNumber to iterNumber + 1
if iterNumber is 1 then
showInfoAbout(defaultPos)
end if
set theResult to choose from list theThemesList default items smartList's getDefaults(false, true, scoThemesListProperties) with title theTitle with multiple selections allowed and empty selection allowed
-- set theResult to {1, 2, 3}
if theResult is false then exit repeat -- or theResult is {} then exit repeat
set aRes to smartList's updateList(theResult, theThemesList, scoThemesListProperties, true)
smartList's updateList(aRes, relatedLIst, scoThemesListProperties, false)
set theResult to choose from list relatedLIst default items smartList's getDefaults(false, true, scoThemesListProperties) with title theTitle with multiple selections allowed and empty selection allowed
if theResult is false then exit repeat -- or theResult is {} then exit repeat
set aRes to smartList's updateList(theResult, relatedLIst, scoThemesListProperties, true) -- last updated which original were true, uses this to update with.
set aRes to smartList's updateList(aRes, theThemesList, scoThemesListProperties, false) -- last updated which original were true, uses this to update with.
if not (iterNumber is 1 or iterNumber is less than 3) then
if iterNumber is 3 then
display dialog "Deletions begins"
set theTitle to "Choose Items from a Shrinking List"
display dialog "Deletes first element."
set theThemesList to rest of theThemesList
set relatedLIst to rest of relatedLIst
set listChanged to true
else if iterNumber is 4 then
display dialog "Deletes last element"
set theThemesList to items 1 thru -2 of theThemesList
set relatedLIst to items 1 thru -2 of relatedLIst
set listChanged to true
else if iterNumber is 5 then
display dialog "deletes element nr 3"
set theThemesList to items 1 thru 2 of theThemesList & items 4 thru -1 of theThemesList
set relatedLIst to items 1 thru 2 of relatedLIst & items 4 thru -1 of relatedLIst
set listChanged to true
else if iterNumber is 6 then
set theTitle to "Choose Items from a Growing List"
display dialog "inserts element at front"
set theThemesList to getAThemesItemWhichIsMissing(origTheThemesList, theThemesList) & theThemesList -- {"System Properties"}
set relatedLIst to getARelatedItemWhichIsMissing(origRelatedLIst) & relatedLIst
set listChanged to true
else if iterNumber is 7 then
display dialog "inserts element at the end"
set theThemesList to theThemesList & getAThemesItemWhichIsMissing(origTheThemesList, theThemesList) -- {"Application - Script Models"}
set relatedLIst to relatedLIst & getARelatedItemWhichIsMissing(origRelatedLIst) -- {"chapter 7"}
set listChanged to true
else if iterNumber is 8 then
display dialog "inserts element into third position"
set theThemesList to items 1 thru 2 of theThemesList & getAThemesItemWhichIsMissing(origTheThemesList, theThemesList) & items 3 thru -1 of theThemesList
set relatedLIst to items 1 thru 2 of relatedLIst & getARelatedItemWhichIsMissing(origRelatedLIst) & items 3 thru -1 of relatedLIst
set listChanged to true
else if iterNumber is 9 then
set theTitle to "Choose Items from a Static List"
if defaultPos > -1 then
set defaultPos to defaultPos - 1
copy origTheThemesList to theThemesList
copy origRelatedLIst to relatedLIst
set scoThemesListProperties to smartList's scoMakeListPropertyObject(defaultPos, theThemesList)
set iterNumber to 0
else
display dialog "All Done!
I really hope you lfind it useful.
Enjoy! McUsr © 2010 and put in Public Domain" with title scriptTitle
return
end if
end if
end if
if listChanged is true then
-- I update the list based on the list which last were selected something from.
set scoThemesListProperties to smartList's updateListProperties(theThemesList, scoThemesListProperties)
set listChanged to false
end if
end repeat
-- helper functions for the demo.
on getAThemesItemWhichIsMissing(theOrigList, theCurrentList)
script o
property l : theOrigList
property m : theCurrentList
end script
global uniqueItemPos
local thisRes
set uniqueItemPos to 1
repeat with i from 1 to (count theOrigList)
if contents of item i of o's l is not in o's m then
set uniqueItemPos to i
set thisRes to contents of item i of o's l
exit repeat
end if
end repeat
return thisRes as list
end getAThemesItemWhichIsMissing
on getARelatedItemWhichIsMissing(theOrigList)
global uniqueItemPos
return contents of item uniqueItemPos of theOrigList as list
end getARelatedItemWhichIsMissing
script smartList
on scoMakeListPropertyObject(intDefaultChoicePosition, lstListValues)
script o
property lastValues : {} -- the last selected values
property origPositions : {} -- the positions in the list were the last selected values was found before they were moved into new positions.
property actualPositions : {} -- the positions the chosen_items held after they are moved.
-- needs this list in order to adjust for growage or shrinkage. Only updates this when we create a new list.
property defaultPositon : intDefaultChoicePosition -- the position to use when only one selected item else - "positioning scheme"
-- positive number -> from pos and downwards when multiple.
-- zero --> centered around center of list.
-- negative --> from pos and downwards if possible when multiple.
property lHalf : 0
property lRest : 0
property listCount : 0
end script
set o's listCount to count of lstListValues
set o's lHalf to (o's listCount) div 2
set o's lRest to (o's listCount) mod 2
o
end scoMakeListPropertyObject
on updateListProperties(lstListValues, scoSmartListProperties)
-- may change every property but the defaultChoicePosition
-- filters out any old values which aren't there any more from previous lastValues.
-- adjust original positions with regard to the new positions of the items this should be by a factor of 1 or -1.
-- You are REQUIRED TO CALL THIS ROUTINE whenever you ADD OR DELETE ON SINGLE ELEMENT TO THE LIST.
script o
property l : lstListValues
property oldActualPositionsList : {} -- the old positons, nees this to adjust relations among lists.
property newActualPositionList : {} -- the new actual positons in list -- for same reasons.
property newOrigPositions : {} -- stores the updated positions here until we copy them back.
property newLastValuesList : {} -- The new list of chosen values, should any have been deleted. during shrink.
end script
local newDefaultValCount, theDiff, newListCount, tmp_list, thisElement, oldOriginalValues, marker
set newListCount to count of lstListValues
if newListCount ≠scoSmartListProperties's listCount then
set theDiff to newListCount - (scoSmartListProperties's listCount)
set scoSmartListProperties's listCount to newListCount
set scoSmartListProperties's lHalf to newListCount div 2
set scoSmartListProperties's lRest to newListCount mod 2
copy o's l to tmp_list
set text item delimiters to return
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
set newDefaultValCount to 0
repeat with i from 1 to (count scoSmartListProperties's lastValues)
set thisElement to contents of item i of scoSmartListProperties's lastValues
if thisElement is in items of o's l then
set newDefaultValCount to newDefaultValCount + 1 -- counting up the elements left if shrinkage.
copy thisElement to end of o's newLastValuesList
set end of o's newActualPositionList to -1 + (count (paragraphs of (text 1 thru (offset of (return & thisElement & return) in tmp_list) of tmp_list)))
copy contents of item i of scoSmartListProperties's origPositions to end of o's newOrigPositions
copy contents of item i of scoSmartListProperties's actualPositions to end of o's oldActualPositionsList
end if
end repeat
-- what we got so far is any elements left in the last chosen values list together with
-- corresponding elements of the original and actual positions. we will now use the difference
-- between the currently actual positions and the old actual positions to deduce how we must adjust
-- the original positions to be in sync with the current state of the list.
set marker to newDefaultValCount + 1
repeat with i from 1 to newDefaultValCount
if contents of item i of o's newActualPositionList is not contents of item i of o's oldActualPositionsList then
set marker to contents of item i of o's oldActualPositionsList
exit repeat
end if
end repeat
-- here we adjust the positons in the newOrigPositons list according to our findings.
-- we are bruteforcing our way through with no regards for order.
repeat with i from 1 to newDefaultValCount
if contents of item i of o's newOrigPositions ≥ marker then
set item i of o's newOrigPositions to (contents of item i of o's newOrigPositions) + theDiff
end if
end repeat
copy o's newActualPositionList to scoSmartListProperties's actualPositions
copy o's newLastValuesList to scoSmartListProperties's lastValues
copy o's newOrigPositions to scoSmartListProperties's origPositions
end if
return scoSmartListProperties
end updateListProperties
on getDefaults(blnReturnSingle, blnOriginalList, scoSmartListProperties)
if blnReturnSingle is true then
local itmCount
set itmCount to (count scoSmartListProperties's lastValues)
if itmCount is 0 then return {}
if scoSmartListProperties's defaultPositon < 0 then
return item itmCount of scoSmartListProperties's lastValues as list
else
return item 1 of scoSmartListProperties's lastValues as list
end if
else
return scoSmartListProperties's lastValues as list
end if
end getDefaults
-- Updates the order of the values of the list: based upon properties in the scriptObject scoSmartListProperties
-- It manages two kinds of lists;
-- Original lists, which are the list which the item was selected from in the choose from list dialog.
-- Related lists, which are lists which are in a 1 to 1 relation with the Original list.
-- When fed either type of list it will:
-- return the original positions of the items moved to maintain order among multiple lists.
-- Those values are also stored internally in a list in the scriptObject scoSmartListProperties.
-- The same goes for: the selected values, and their actual postions in the arranged list
-- IMPLICATIONS:
-- When updating the properties of a list if it has grown or shrunk, the operations must be performed
-- in an orderly manner:
-- THE LAST LIST YOU USED updateList() ON SHOULD BE THE SAME LIST YOU USE TO UPDATE THE scoSmartListProperties
-- SCRIPT OBJECT BY THE updateProperties() HANDLER WITH.
on updateList(lstChosenItems, lstListValues, scoSmartListProperties, blnIsOriginalList)
script o
property l : lstListValues
property saved_list : {}
end script
local choicesCount, listCount, intDefaultItemPosition, startpos, tmp_list, item_index, step, i, thisElement, newActualPositons, newOrigPositions, savedOrigPositions
if lstChosenItems is false then
tell me to display alert "listUpdater's updateList: I really shouldn't be given a list with one value of false... I quit!"
error number -128
end if
set choicesCount to (count of lstChosenItems)
set listCount to scoSmartListProperties's listCount
if choicesCount is 0 then
return {}
else if choicesCount > listCount then
tell me to display alert "listUpdater's updateList: There were more chosen items than elements in the list supposed to be chosen from.I quit!"
error number -128
end if
set intDefaultItemPosition to scoSmartListProperties's defaultPositon
-- ** PRELIMNARY calculations for adjusting any optimistic whishes the user may have to what the list really contains .
-- ex. wants the chosen item to be placed in last positon and have chosen 3 ...
if intDefaultItemPosition < 0 then -- then relative addressing from the end -1 is last element and so on.
if listCount + intDefaultItemPosition < 0 then
set startpos to listCount
else
set startpos to listCount + intDefaultItemPosition + 1
end if
-- chosen items should effectively end in the final position.
set startpos to startpos + 1 - choicesCount
else if intDefaultItemPosition is 0 then -- calculates start position relative to center of list.
local cHalf, cRest, lHalf, lRest
set cHalf to choicesCount div 2
if cHalf is 0 then set cHalf to 1
set lHalf to scoSmartListProperties's lHalf
if lHalf is 0 then set lHalf to 1
set cRest to choicesCount mod 2
set lRest to scoSmartListProperties's lRest
if cRest - lRest = 0 then
set startpos to (lHalf + 1 - cHalf)
else if cRest = 1 then -- lCount even and minimum 2
set startpos to (lHalf - cHalf)
else if lRest = 1 then -- cCount even
set startpos to (lHalf - cHalf + 1)
end if
else -- intDefaultItemPosition > 0 rather vanilla positioning.
local posAdjust
if intDefaultItemPosition > listCount then
set intDefaultItemPosition to listCount
end if
set startpos to intDefaultItemPosition
-- ensures that the list can actualla fit into free slots lefte, otherwize adjust startpos upwards.
set posAdjust to (intDefaultItemPosition + choicesCount - 1) - listCount
if posAdjust > 0 then
set startpos to startpos - posAdjust
else
set startpos to intDefaultItemPosition -- starts at that position and works downwards
end if
end if
if blnIsOriginalList is false then -- we are updated an related list based upon the changes we did on the list we choosed items from.
-- This is a rather odd situation, since we can have lists we never display.
if class of item 1 of lstChosenItems is not integer then
tell me to display alert "listUpdater's updateList: The chosen items were not numbers as they should be when the list is a *related list*. I quit!"
error number -128
else
-- arrangements for making the centering work in ALL situations.
-- creates a new set of lastValues based upon origPositions, opposite to below. if intDefaultPosition < 1 items in origpositions is in order.
set scoSmartListProperties's lastValues to {} -- makes a new set of saved values.
if scoSmartListProperties's origPositions is {} then return {}
-- we got some original positions, which is what we are feeding on,
if intDefaultItemPosition is 0 then
-- special precautions to make centering the chosen item it always work, this is tricky because the first
-- items in the list may be chosen, so we have to move the items to the end before setting them
-- into the correct positions. At the moment I see now way of combining this with the
-- the branch covering elements to be sat with absolute or relative positioning
set newOrigPositions to {}
copy scoSmartListProperties's origPositions to savedOrigPositions -- ! saved
copy o's l to tmp_list -- only does this when we have to!
-- retain original positions of the items.
set text item delimiters to return -- credits Emmanuel Levy's indexof() handler
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
repeat with choice_item_index from 1 to choicesCount -- what's in this loop is mainly due to Nigel Garvey.
-- we put the elements reversed onto the end of list before setting the into correct postions.
set item_index to listCount - choice_item_index + 1 -- Cater for reverse ordering from the end of the list.
set end of newOrigPositions to item_index
set thisElement to item (item (choicesCount - choice_item_index + 1) of savedOrigPositions) of o's l
set i to -1 + (count (paragraphs of (text 1 thru (offset of (return & thisElement & return) in tmp_list) of tmp_list)))
-- Does it have to be moved forwards or backwards (or neither)?
if (item_index > i) then
set step to 1
else
set step to -1
end if
-- Move the intervening items up or down by one.
repeat with i from i to (item_index - step) by step
set item i of o's l to item (i + step) of o's l
end repeat
-- Insert the item into the required location.
set item item_index of o's l to thisElement
copy o's l to tmp_list -- The superfluos one will be used below!
set text item delimiters to return
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
end repeat
copy newOrigPositions to scoSmartListProperties's origPositions
repeat with choice_item_index from 1 to choicesCount
set item_index to startpos + choice_item_index - 1 -- static positon, the list arranges for us.
set thisElement to item (listCount - choicesCount + choice_item_index) of o's l
set end of scoSmartListProperties's lastValues to thisElement
set i to -1 + (count (paragraphs of (text 1 thru (offset of (return & thisElement & return) in tmp_list) of tmp_list)))
-- Does it have to be moved forwards or backwards (or neither)?
if (item_index > i) then
set step to 1
else
set step to -1
end if
-- Move the intervening items up or down by one.
repeat with i from i to (item_index - step) by step
set item i of o's l to item (i + step) of o's l
end repeat
-- Insert the item into the required location.
set item item_index of o's l to thisElement
end repeat
-- after care
copy savedOrigPositions to scoSmartListProperties's origPositions
return scoSmartListProperties's origPositions
-- Shouldnt be necessary to do anything with actuaal positions.
else
copy o's l to o's saved_list -- can be optimized but ...
repeat with choice_item_index from 1 to choicesCount
copy o's l to tmp_list -- only does this when we have to!
-- retain original positions of the items.
set text item delimiters to return -- credits Emmanuel Levy's indexof() handler
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
if intDefaultItemPosition < 0 then
set item_index to (startpos + choicesCount - choice_item_index)
else
set item_index to (startpos + choice_item_index - 1)
end if
set thisElement to item (item choice_item_index of scoSmartListProperties's origPositions) of o's saved_list
set end of scoSmartListProperties's lastValues to thisElement
set i to -1 + (count (paragraphs of (text 1 thru (offset of (return & thisElement & return) in tmp_list) of tmp_list)))
-- Does it have to be moved forwards or backwards (or neither)?
if (item_index > i) then
set step to 1
else
set step to -1
end if
-- Move the intervening items up or down by one.
repeat with i from i to (item_index - step) by step
set item i of o's l to item (i + step) of o's l
end repeat
-- Insert the item into the required location.
set item item_index of o's l to thisElement
end repeat
return scoSmartListProperties's origPositions
end if
end if
else if blnIsOriginalList is true then
-- creates new orig positions based upon lstChosenItems to use from a related list.
if intDefaultItemPosition < 0 then -- NEW center works.
set lstChosenItems to reverse of lstChosenItems
end if
copy lstChosenItems to scoSmartListProperties's lastValues
copy o's l to tmp_list
set scoSmartListProperties's origPositions to {}
-- retain original positions of the items.
set text item delimiters to return -- will be leveraged below.
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
repeat with i from 1 to choicesCount -- loop credits Emmanuel Levy's indexof() handler
try
set end of scoSmartListProperties's origPositions to -1 + (count (paragraphs of (text 1 thru (offset of (return & item i of scoSmartListProperties's lastValues & return) in tmp_list) of tmp_list)))
on error
tell me to display alert "listUpdater's updateList: There were an element in the choices list that were not present in the list of possible choices. I quit!"
error number -128
end try
end repeat
if intDefaultItemPosition is 0 then -- must assure that the elements are placed AFTER startpos in order for NEXT STEP to work.
-- we move every item which is to be centered to then end of the list in reverse order in order for them
-- to be positioned correctly.
set newOrigPositions to {}
copy scoSmartListProperties's origPositions to savedOrigPositions -- ! saved
repeat with choice_item_index from 1 to choicesCount -- what's in this loop is mainly due to Nigel Garvey.
set item_index to listCount - choice_item_index + 1 -- Cater for reverse ordering from the end of the list.
set end of newOrigPositions to item_index
set i to -1 + (count (paragraphs of (text 1 thru (offset of (return & item choice_item_index of scoSmartListProperties's lastValues & return) in tmp_list) of tmp_list)))
-- Does it have to be moved forwards or backwards (or neither)?
if (item_index > i) then
set step to 1
else
set step to -1
end if
-- Move the intervening items up or down by one.
repeat with i from i to (item_index - step) by step
set item i of o's l to item (i + step) of o's l
end repeat
-- Insert the item into the required location.
set item item_index of o's l to item choice_item_index of scoSmartListProperties's lastValues -- listElm -- replacement_item
copy o's l to tmp_list -- The superfluos one will be used below!
set text item delimiters to return
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
end repeat
copy newOrigPositions to scoSmartListProperties's origPositions
end if -- this will work automatically on a related list.
repeat with choice_item_index from 1 to choicesCount -- what's in this loop is mainly due to Nigel Garvey.
if intDefaultItemPosition < 0 then
set item_index to (startpos + choicesCount - choice_item_index)
else
set item_index to (startpos + choice_item_index - 1)
end if
set i to -1 + (count (paragraphs of (text 1 thru (offset of (return & item choice_item_index of scoSmartListProperties's lastValues & return) in tmp_list) of tmp_list)))
-- Does it have to be moved forwards or backwards (or neither)?
if (item_index > i) then
set step to 1
else
set step to -1
end if
-- Move the intervening items up or down by one.
repeat with i from i to (item_index - step) by step
set item i of o's l to item (i + step) of o's l
end repeat
-- Insert the item into the required location.
set item item_index of o's l to item choice_item_index of scoSmartListProperties's lastValues -- listElm -- replacement_item
copy o's l to tmp_list -- The superfluos one will be used below!
set text item delimiters to return
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
end repeat
-- finally we create a list with the actual positions -- needs them for reference should we ever need to
-- update any relations among lists, should an item have been deleted or inserted.
if intDefaultItemPosition is 0 then -- after care
copy savedOrigPositions to scoSmartListProperties's origPositions
end if
set scoSmartListProperties's actualPositions to {}
repeat with choice_item_index from 1 to choicesCount
set end of scoSmartListProperties's actualPositions to -1 + (count (paragraphs of (text 1 thru (offset of (return & item choice_item_index of scoSmartListProperties's lastValues & return) in tmp_list) of tmp_list)))
end repeat
end if
return scoSmartListProperties's origPositions
end updateList
” A simpler version that returns the elements of a second 1:1 related list
” based upon a selection of items to first.
” The two lists must be corresponding item by item.
”PARAMETERS:
” lstItemsFromFirst : The items from the first list we want to find corresponding items in the second for.
” lstTheFirstList : The first list
” lstTheSecondList: The second list
” RETURNS:
” Any corresponding items.
on getRelatedItems(lstItemsFromFirst, lstTheFirstList, lstTheSecondList)
script o
property theItems : lstItemsFromFirst
property firstList : lstTheFirstList
property secondList : lstTheSecondList
end script
local itemsCount, firstListCount, secondListCount, tmp_list, origPositions, newList, step, i, thisElement, newActualPositons, savedOrigPositions
-- initial qualification of parameters.
if lstItemsFromFirst is false then
tell me to display alert "smartList's getRelatedItems: I really shouldn't be given a list with one value of false... I quit!"
error
end if
set itemsCount to (count of lstItemsFromFirst)
set firstListCount to (count of lstTheFirstList)
set secondListCount to (count of lstTheSecondList)
-- secondary qualification of parameters
if firstListCount ≠secondListCount then
tell me to display alert "smartList's getRelatedItems: I really shouldn't be given lists with different number of items : firstList : " & firstListCount & " second list : " & secondListCount & " I quit!"
error
else if itemsCount > firstListCount then
tell me to display alert "smartList's getRelatedItems: The number of the chosen items is " & itemsCount & " which is more than there is in the actual list : " & firstListCount & " I quit!"
error
end if
if itemsCount is 0 then
return {}
end if
-- The rest of the error conditions would now be that we couldn't find items in the lstTheFirstList which were in the lstItemsFromFirst
-- get the positions of the elements in the first list.
copy o's firstList to tmp_list
set text item delimiters to return -- credits Emmanuel Levy's indexof() handler
set tmp_list to return & tmp_list & return
set text item delimiters to {""}
set origPositions to {}
repeat with choice_item_index from 1 to itemsCount
set end of origPositions to -1 + (count (paragraphs of (text 1 thru (offset of (return & (item choice_item_index of o's theItems) & return) in tmp_list) of tmp_list)))
end repeat
if 0 is in origPositions then
tell me to display alert "smartList's getRelatedItems: There were items in the items from first list which I didn't find in the first - base - list! I quit!"
error
end if
” Retrieves the corresponding items from the second list.
set newList to {}
repeat with aPosition in origPositions
set end of newList to contents of item aPosition of o's secondList
end repeat
return newList
end getRelatedItems
end script
on showInfoAbout(positioningType)
local scriptTitle
set scriptTitle to "smartListDemo"
if positioningType is -1 then
display dialog "¢ The default position is now specified to be in the
last position of the list.
¢ A chosen element or elements will be moved to
or towards this position.
¢ This positions is specified relative to the
end of the list.
¢ It is possible to specify f.ex position -3 to place
an element in the pos 3 elements above the end
of the list." with title scriptTitle
else if positioningType is 0 then
display dialog "¢ The default position now specified to be in the
middle of the list.
¢ A chosen element will be moved to or towards
this position centred around the middle.
¢ This functionality can be obtained by specifying
a default position of 0."
else if positioningType is 1 then
display dialog "Observe that the lists grow and shrink under the demo. Please also observe that the corresponding element in the related list -either the themes list or the chapter list, are updated with the corresponding elment in the same position.
Pleas try to select elements, single and multiple and combinations thereof, and watch the results as the list is growing or shrinking.
I hope you have the patience to go through the somewhat boring and maybe far to long demo.
Thanks - McUsr" with title scriptTitle
display dialog "¢ The default position is now specified to be in the
first position of the list.
¢ A chosen element or elements will be moved to
or towards this position.
¢ This position is specified absolutely in the list.
¢ It is possible to f.ex specify pos nr 3." with title scriptTitle
end if
end showInfoAbout
Hopefull you have now sensed that the code for this functionality is very transparent to what your code really is meant to do and doesn’t obstruct it in any way.
This is specific to the handler call, which delivers the default items, in your choose dialog call.
You can provide an integer specifying the accurate or relative position in the list that the default item should have. Note, you can also use the number 0 to specify the middle position in the list.
If you specify the middle position, then if you had selected more than one item from last choose dialog, then the elements will be positioned around the middle position.
About the code
Emmanuel Levy and Nigel Garvey has provided the optimized code for this script object to work pretty fast. All blame, -as the idea for this is mine.
All necessary code is baked into a script object that consists of properties and four handlers (where only two of them is necessary), that’s all you have to do.
– Yeah and it should fairly simple to move the script object into a script server :).
It is fairly robust, however, if the default position submitted does not exist within the list then it will try to put the items as near that position as possible. Negative offsets are allowed, letting you specify for instance a position of “3 or the 3d element from the end, as the default position should you so choose.
The handler is meant to act within your code as transparently as possible. You should be able to just add the handler statements and forget all about it (as shown in example above). Everything is taken care of behind the scenes. A little extra work is required if you have another list which is related to the list with choices, but the single handler call handles that too, you just need to add the extra statements.
script object contents
– documented handler calls.
Sorted Lists
It is very easy to just save the last chosen item, or set of items into a property, which is saved with the script as long as the scriptrunner you use saves the script upon an exit with some return value. (Apples vanilla Script Menu does that.)
Example of retaining last chosen items in a sorted list.
Best Regards
McUsr