Script for managing Stacks
***Edit*** 2020/07/05 The migrated version of this script likely will not complile because the Applescript symbols for "not equal", "less than" and "greater than" are not supported in the new forum
Capture One users have frequently mentioned in the forums that they seek a solution to group only selected variants, not every variant of some images.
Many of us also look for a DAM function known as "stacking", which groups a number of variants of different images. But not necessarily all the variants of those images.
Likely these features have been requested from Phase One, but no one knows if and when they might be delivered in Capture One Pro. Phase One understandably has a many business priorities to manage, and their R&D spend has to be managed (as does virtually every other commercial enterprise).
As an interim measure I provide this solution.
I define a Stacked Variants Album as a Smart Album which has a rule that selects variants with its own name in the Metadata.
In this partiicular implementation, the SVAs serch for their own name in the IPTC Subject Code field, which can convenintly hold a number of such strings. The SVA's names all start with "SVA_" so that a user can easily recognise them.
As one of my correpondents has pointed out, all of this can be done by hand without a script.
This script takes care of in a few seconds what will take one a few minutes by hand. And if you are interuppted by a client call or by family matters, the script won't forget or corrupt the data, it will still execute accurately.
The script will create SVA, and it will add selected variants from SVAs, and remove them. There is a fairly efficient dialog that will let the user choose the script's actions.
The entire script is too long for one posting, so I have boroken it into two parts. This is Part 1.
Part 2 is in the next posting.Note that in this code, there is a handler findParentColl() which obtains the parent collections of a collection - this information is not directly available in any Capture One Applescript command.
## Applescript to create a Stacked Variants Album (SVA) from Selected Variants
## Version 12.33 !! NO GUARANTEE OF SUPPORT !! Best effort
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
-- Script Dialog
----"xxxx" adds the selected variants to the smart album "SVA_xxxx"
---- Shortcuts are: "+","-","<",">"
---- Shortcuts must be at the beginning
---- ">" adds clones of selected variants
---- "<" adds clones of selected variants with adjustments reset
---- "+" sets the SVA smart album using the title of the current collection:
---- If the current collection is not an SVA, then the SVA will be "SVA_thisCollectionName"
---- If the current collection is an SVA with no number then the SVA will be "SVA_thisCollectionName_1"
---- If the current collection is an SVA with a number then the number of the new SVA will be incremented
---- "#nnn" at the end adds the variants to an SVA ending with "_nnn"
---- "+xxx" adds the variants to an SVA ending with "xxx"
---- --- n are numbers, x are letters
---- "-" removes selected variants instead of adding them
---- The short cut "->" selects the removed variants in the parent project.
---- "?" generates a help menu with the short cut commands
-- *** Script Function and Definitions
-- An SVA is a Smart Album configured to select variants with its own name
-- The SVAs created here search in the variants' IPTC Subject Code field
-- The defaults is that the SVA is created (if not present) in the "nearest" parent project or catalog
-- Variants are added to an SVA by adding the SVA's full name to their IPTC Subject Code field
-- Variants are removed from an SVA by removing the SVA's full name from their IPTC Subject Code field
-- *** User Protection
-- No variants are ever deleted. No collections are ever deleted
-- The variants IPTC Subject Code field is never set to ""
-- *** Use
-- select a Project, an album or smart album, select some variants
-- run this script - for starters, just use the default "+" in the dialog
-- ***To Setup
-- Start Script Editor, open a new (blank) file, copy and paste both parts into one Script Editor Document, compile (hammer symbol) and save.
-- Best if you make "Scripts" folder somewhere in your Documents or Desktop
-- This file is suitable to use as an application in Capture One Pro's Script Menu
-- *** Operation in Script Editor
-- Open the compiled and saved document
-- Open the Script Editor log window, and select the messages tab
-- The user may elect to set defaults for enabling or disabling results in Notifications, TextEdit and Script Editor by setting the "enable" variables at beginning of the script
-- The user may change the default amount of reporting by setting the "debugLogLevel" and "ResultsFileMaxDebug" variables at beginning of the script
-- If you are having some issues, then set debugLogLevel to 3 and send me the results from Script Editors log window, or Text Edit.
use AppleScript version "2.5"
use scripting additions
property svaDialogText : "+"
set loqGUIsettings_L to my setupLoqqing5() -- setup the variables used for Loqqing system
## Values in this section are safe to change, within limits indicated. Support is likely but no commitment
## Default values
set Loqqing's debugLogLevel to 0 -- 0...6 Values >1 result in increasing amounts of debug data that takes longer to report
set Loqqing's enableResultsFile to false -- (true/false)
set Loqqing's enableResultsByClipboard to false -- (true/false)
set Loqqing's enableNotifications to true -- (true/false) - enable notifications of errors and exceptions
set Loqqing's notificationsMaxDebug to 1 -- 0...6 suggest not more than 1
set Loqqing's ResultsFileMaxDebug to 3 -- 0...6 suggest not more than 2
set Loqqing's enableResultsByNotifications to true
set Loqqing's enableResultsByLoq to true
set Loqqing's enableResultsByDialog to false -- (true/false)
set Loqqing's maxDialogPercent to 85 -- (0% to 100% of the monitor) The amount of data that triggers a dialog report
set nameSVA to "SVA"
set svaDialogSeparator to "#" -- used to separate the counter part of the dialog
set charSeparator to "_" -- used to separate the counter part of the SVA name
set svaMaxCount to 99 -- maximum of 99 SVAs with the same basename - can be set higher
set svaEnableFindParents to true -- allow seraching outside the current collection
set svaGroupsAsParents to false -- allow Groups as parent collections for SVAs
## ***** Not safe to change stuff below this line, unless you have some background in SW development.
## I generally won't help much if you change stuff below this line. I may explain the design intent.
set svaParentKindList to {"catalog", "project"}
if svaGroupsAsParents then set svaParentKindList to {"catalog", "group", "project"}
set debugLogEnable to (0 < Loqqing's debugLogLevel)
## Result reporting methods which are valid for this script
set Loqqing's gateResultsFile to true
set Loqqing's gateResultsByClipboard to true
set Loqqing's gateResultsNotification to true
set Loqqing's gateResultsDialog to true
## Reporting methods which are NOT valid for this script
-- disable the reporting method
-- disable user control
tell application "System Events" to set parent_name to name of current application
set Script_Title to (get name of me)
set Result_DocName to "CO_SVA_Report.txt"
set debugLogEnable to (0 < Loqqing's debugLogLevel)
set loqResultMethod to my InitializeLoqqing5(Result_DocName, Script_Title) -- Initialize the results logging system
set {minCOPversion, maxCOPversion} to {"12", "12"}
tell my validateCOP5(minCOPversion, maxCOPversion)
if its hasErrors then error my loqqed_Error_Halt5(get its errorText)
set {theAppName, copVersion} to {its theAppName, its copVersion}
end tell
tell application "Capture One 12" to set COPDocRef to get current document
tell my validateCOPdoc5(COPDocRef, {"Catalog"})
if its hasErrors then error my loqqed_Error_Halt5(get its errorText)
set {COPDocName, COPDocKind_s} to {its COPDocName, its COPDocKind_s}
end tell
tell my validateCOPcollections5(COPDocRef)
set {selectedCollectionRef, kindSelectedCollection_s, nameSelectedCollection} to ¬
{its selectedCollectionRef, its kindSelectedCollection_s, its nameSelectedCollection}
end tell
set {ruleSvaParts, criteriaSvaParts} to {{}, {}}
set ruleSvaParts to ruleSvaParts & "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_CONTENT_SUBJECTCODE</Key><Operator>16</Operator><Criterion>"
set ruleSvaParts to ruleSvaParts & "</Criterion></Condition></MatchOperator></MatchOperator>"
set criteriaSvaParts to criteriaSvaParts & "<Key>IB_S_CONTENT_SUBJECTCODE</Key><Operator>16</Operator><Criterion>"
set criteriaSvaParts to criteriaSvaParts & "</Criterion>"
##Fix!!
tell application "Capture One 12" to set {refSelVariantsL, nameSelVariantsL, idSelVariantsL, svaSelVariantsL} to {it, name, id, content subject codes} of (variants whose selected is true)
set countSelectedVariants to count of idSelVariantsL
repeat
set userCancelled to false
try
set svaDialogResult to display dialog "Name of Stacked Variants Album to add these variants to?" default answer svaDialogText
on error number -128
set userCancelled to true
exit repeat
end try
if ((text returned of svaDialogResult) does not contain "?") then exit repeat
try
display dialog getHelp()
on error number -128
set userCancelled to true
exit repeat
end try
end repeat
if userCancelled then
my loqThis(-1, true, "User cancelled the script")
else
##Process the dialog results
set {svaDialogText, svaDialogControlChars, svaDialogHasArg, svaDialogArg, svaDialogArgLength} to {removeLeadingTrailingSpaces(text returned of svaDialogResult), "", false, "", 0}
set {dialogTextLength, svaDialogArgPtr} to {(length of svaDialogText), (1 + (length of svaDialogText))}
if debugLogEnable then my loqThis(2, false, "svaDialogText "" & svaDialogText & """)
if 0 = dialogTextLength then
my loqThis(-1, true, "Name of Stacked Variants Album was empty and no shortcut")
else
## Extract shortcuts
set cmdL to {"-", "+", "<", ">"}
repeat with textPtr from 1 to dialogTextLength
if (cmdL does not contain item textPtr of svaDialogText) then
set textPtr to textPtr - 1
exit repeat
end if
end repeat
set svaDialogArgPtr to textPtr + 1
if textPtr > 0 then set svaDialogControlChars to (text 1 thru textPtr of svaDialogText)
## Extract Dialog Argument String
if dialogTextLength ≥ svaDialogArgPtr then set svaDialogArg to text svaDialogArgPtr thru -1 of svaDialogText
set svaDialogArgLength to length of svaDialogArg
if svaDialogArgLength > 0 then set svaDialogHasArg to true
if debugLogEnable then my loqThis(2, false, "svaDialogHasArg:" & svaDialogHasArg & " svaDialogArg: "" & svaDialogArg & "" svaDialogControlChars: "" & svaDialogControlChars & """)
## Extract the Dialog Counter
set {svaDialogHasCtr, svaDialogCtr} to {false, 0}
if svaDialogArgLength ≥ 2 then
if svaDialogHasArg then
repeat with textPtr from svaDialogArgLength to 0 by -1
if 0 = textPtr then exit repeat -- svaDialogArg is all numbers
set textid to id of text textPtr of svaDialogArg
if (textid < 48) or (textid > 57) then exit repeat
end repeat
if (textPtr > 0) and (svaDialogSeparator = (text textPtr of svaDialogArg)) then -- found a counter
set {svaDialogHasCtr, svaDialogCtr} to {true, (0 + (text (textPtr + 1) thru -1 of svaDialogArg))}
if (1 = textPtr) then
set {svaDialogHasArg, svaDialogArg, svaDialogArgLength} to {false, "", 0}
else
set svaDialogArg to removeLeadingTrailingSpaces2((text 1 thru (textPtr - 1) of svaDialogArg), false, true)
set {svaDialogHasArg, svaDialogArgLength} to {not ("" = svaDialogArg), (length of svaDialogArg)}
end if
if debugLogEnable then my loqThis(2, false, "svaDialogHasArg:" & svaDialogHasArg & " svaDialogArg: "" & svaDialogArg & """)
end if
end if
end if
if debugLogEnable then my loqThis(2, false, "svaDialogHasCtr:" & svaDialogHasCtr & " svaDialogCtr:" & svaDialogCtr)
##Determine Script Actions from the Shortcuts
set {delSvaTag, cloneVariant, resetCloneAdj, svaFromCC, selDelVariants} to {false, false, false, false, false}
if "" ≠svaDialogControlChars then
if (svaDialogControlChars contains "+") then
set svaFromCC to true
if (svaDialogControlChars contains ">") then set cloneVariant to true
if (svaDialogControlChars contains "<") then set {cloneVariant, resetCloneAdj} to {true, true}
end if
if (svaDialogControlChars contains "-") then
set delSvaTag to true
set {cloneVariant, resetCloneAdj} to {false, false}
if (svaDialogControlChars contains ">") then set selDelVariants to true
end if
end if
if debugLogEnable then my loqThis(2, false, "delSvaTag: " & delSvaTag & "; cloneVariant: " & cloneVariant & "; resetCloneAdj: " & resetCloneAdj & "; svaFromCC: " & svaFromCC & "; selDelVariants: " & selDelVariants)
##Process the Current Collection Name
set prefixSVA to nameSVA & charSeparator
set {lenPrefixSVA, collNameIsSVA, lengthCollName} to {(length of prefixSVA), false, (get length of nameSelectedCollection)}
## Extract the Collection Base Name and determine if it is an SVA
if (lenPrefixSVA < lengthCollName) and (prefixSVA = (get text 1 thru lenPrefixSVA of nameSelectedCollection)) then
set collNameIsSVA to true -- Current collection has an SVA type name
##Get the current SVA collection's counter
## if the last characters of the name are the separator charactor, "_", followed by numbers, then it is an SVA counter.
repeat with textPtr from lengthCollName to 1 by -1
set textid to id of text textPtr of nameSelectedCollection
if (textid < 48) or (textid > 57) then exit repeat
end repeat
set textPtr to (0 + textPtr) -- dereference the result
if (textPtr < lengthCollName) and (charSeparator = (text textPtr of nameSelectedCollection)) then
##There is a counter
set {collnCtr, collnIsCounted} to {(0 + (text (textPtr + 1) thru -1 of nameSelectedCollection)), true}
set collnBaseName to text (lenPrefixSVA + 1) thru (textPtr - 1) of nameSelectedCollection -- separator character excluded
else
set {collnIsCounted, collnCtr, collnBaseName} to {false, 0, (text (lenPrefixSVA + 1) thru -1 of nameSelectedCollection)}
end if
else
set {collNameIsSVA, collnIsCounted, collnCtr, collnBaseName} to {false, false, 0, nameSelectedCollection}
end if
if debugLogEnable then my loqThis(2, false, "collnBaseName: "" & collnBaseName & "" collnIsCounted:" & collnIsCounted & " collnCtr:" & collnCtr & " collNameIsSVA:" & collNameIsSVA)
## Determine the counter of the Target SVA
if svaDialogHasCtr then
set ctrTargetSva to svaDialogCtr
else if collNameIsSVA then
set ctrTargetSva to (collnCtr + 1)
if delSvaTag then set ctrTargetSva to (collnCtr + 0)
else
set ctrTargetSva to 0
end if
## Determine the base name of the Target SVA
if svaFromCC then
set baseNameTargetSva to (prefixSVA & collnBaseName & svaDialogArg)
else if svaDialogHasArg then
set baseNameTargetSva to (prefixSVA & svaDialogArg)
else
set baseNameTargetSva to (prefixSVA & collnBaseName)
end if
## Determine the full name of the Target SVA
copy baseNameTargetSva to nameTargetSva
if 0 < ctrTargetSva then set nameTargetSva to nameTargetSva & charSeparator & (ctrTargetSva)
if debugLogEnable then my loqThis(2, false, "nameTargetSva: "" & nameTargetSva & """ & " baseNameTargetSva:"" & baseNameTargetSva & "" ctrTargetSva:" & ctrTargetSva)
##EXECUTION
if delSvaTag then -- Remove Variants from an SVA
if debugLogEnable then my loqThis(1, false, "Remove Variants from "" & nameTargetSva & """)
if (0 = countSelectedVariants) then
my loqThis(-1, true, "No variants selected, none removed")
else
repeat with cntVariants from 1 to count of refSelVariantsL
set {theSvaList_L, theVariantName} to {(my splitstringtolist((item cntVariants of svaSelVariantsL), ",")), (item cntVariants of nameSelVariantsL)}
if (theSvaList_L contains nameTargetSva) then
tell application "Capture One 12" to tell (item cntVariants of refSelVariantsL) to set content subject codes to my joinlisttostring(my removeItemFromList(theSvaList_L, nameTargetSva), ",")
if debugLogEnable then my loqThis(1, false, "Removed tag for "" & nameTargetSva & "" from Variant "" & theVariantName & """)
else
if debugLogEnable then my loqThis(1, false, "Tag for "" & nameTargetSva & "" was not present in Variant "" & theVariantName & """)
end if
end repeat
if selDelVariants then
set {svaEnableCreate, svaEnableCounting} to {false, false} -- find exactly this collection, don't create it
set {theSvaRef, foundSva, refSvaParent} to findSvaCollection({selectedCollectionRef}, baseNameTargetSva, svaEnableCreate, svaEnableFindParents, svaEnableCounting, ctrTargetSva, svaMaxCount, charSeparator, svaParentKindList, ruleSvaParts, criteriaSvaParts)
##if foundSva then
tell application "Capture One 12"
if "catalog" = my convertKindList(get kind of refSvaParent) then
tell COPDocRef to set current collection to collection "All Images"
else
tell COPDocRef to set current collection to refSvaParent
end if
deselect COPDocRef variants (variants)
repeat with theVariantID in idSelVariantsL
select COPDocRef variant (variant id ((contents of theVariantID) as string))
end repeat
end tell
##end if
end if
end if
else
## Add Variants to SVA, Create new SVA smart album if needed
if debugLogEnable then my loqThis(1, false, "Searching for "" & nameTargetSva & "" to add variants")
set svaEnableCreate to true
if svaDialogHasCtr then -- Dialog specified the counter value
set svaEnableCounting to false -- don't change or add a counter
else
set svaEnableCounting to true -- change or add a counter as needed
end if
set {theSvaRef, foundSva, refSvaParent} to findSvaCollection({selectedCollectionRef}, baseNameTargetSva, svaEnableCreate, svaEnableFindParents, svaEnableCounting, ctrTargetSva, svaMaxCount, charSeparator, svaParentKindList, ruleSvaParts, criteriaSvaParts)
if not foundSva then
my loqqed_Error_Halt5("Can't find or create the "" & nameTargetSva & "" smart album")
else if (0 = countSelectedVariants) then
my loqThis(-1, true, "No variants selected")
else
set nameTargetSva to name of theSvaRef
if cloneVariant then
tell application "Capture One 12"
repeat with cntVariants from 1 to count of refSelVariantsL
set theVariantRef to (clone variant (item cntVariants of refSelVariantsL) with additive select)
if resetCloneAdj then tell theVariantRef to reset adjustments
set {theSvaList, theVariantName} to {((item cntVariants of svaSelVariantsL) & ""), (item cntVariants of nameSelVariantsL)}
if ((my splitstringtolist(theSvaList, ",")) does not contain nameTargetSva) then
tell theVariantRef to set content subject codes to theSvaList & "," & nameTargetSva
if debugLogEnable then my loqThis(1, false, "Added tag for "" & nameTargetSva & "" to Clone of Variant "" & theVariantName & """)
else
if debugLogEnable then my loqThis(1, false, "Tag for "" & nameTargetSva & "" already present in Variant "" & theVariantName & "" and its clone")
end if
end repeat
deselect COPDocRef variants refSelVariantsL
end tell
else
repeat with cntVariants from 1 to count of refSelVariantsL
set {theSvaList, theVariantName} to {((item cntVariants of svaSelVariantsL) & ""), (item cntVariants of nameSelVariantsL)}
if ((my splitstringtolist(theSvaList, ",")) does not contain nameTargetSva) then
tell application "Capture One 12" to tell (item cntVariants of refSelVariantsL) to set content subject codes to theSvaList & "," & nameTargetSva
if debugLogEnable then my loqThis(1, false, "Added tag for "" & nameTargetSva & "" to Variant "" & theVariantName & """)
else
if debugLogEnable then my loqThis(1, false, "Tag for "" & nameTargetSva & "" already present in Variant "" & theVariantName & """)
end if
end repeat
end if
end if
end if
end if
end if
################ The End ###############
on findSvaCollection(parentRefList, nameTargetColl, enableCreate, enableFindParents, enableCounting, InitCount, MaxCount, separatorChar, kindParentColl_L, saCreateRuleL, saRuleCriteriaL)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to find and create a collection, starting from some arbitrary collection
## Version June 22 2019
global debugLogEnable
local refParentColl, ptrCollRef, thisCollRef, thisCollKind_S, thisCollName, subCollNames, subCollRef, refFoundColl, hasFoundColl, debugText
local collCreated, ctrCollName, nbrTargetCollName, collSearchMatch, collSearchSuccess, collSearchFail, kindTargetColl
local foundSaRules, rulesPtr, saDefRule, refFoundSaColl
copy (contents of item 1 of parentRefList) to thisCollRef
if 1 < (count of parentRefList) then set enableFindParents to false
-- saCreateRule, hasRuleCriteria,saDefRule
-- No rules string -- length 80
--first part is 38 long: "<?xml version="1.0" encoding="UTF-8"?>"
set {saDefRule, lenRuleTest} to {"<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"></MatchOperator>", 38}
if not ((80 < (get length of item 1 of saCreateRuleL)) and ((text 1 thru lenRuleTest of saDefRule) = (text 1 thru lenRuleTest of item 1 of saCreateRuleL))) then error
repeat with ptrCollRef in parentRefList
set thisCollRef to contents of ptrCollRef
tell application "Capture One 12" to tell thisCollRef to set {thisCollKind_S, thisCollName, refParentColl} to {my convertKindList(its kind), its name, it}
set {ctrCollName, collSearchFail, collSearchMatch, collCreated, refFoundColl, hasFoundColl} to {0, false, false, false, {}, false}
set nbrTargetCollName to nameTargetColl & ""
if InitCount > 0 then set nbrTargetCollName to nameTargetColl & separatorChar & InitCount
if debugLogEnable then
tell application "Capture One 12" to tell thisCollRef to set subCollNames to name of its collections
set debugText to "smart album "" & nbrTargetCollName & "" in " & thisCollKind_S & " "" & thisCollName & """
my loqThis(2, false, return & "Find Collection: " & debugText & " with Create: " & enableCreate & " Parent Search: " & enableFindParents & " Counter Increment: " & enableCounting)
my loqThis(3, false, "SubCollections {" & my joinlisttostring(subCollNames, "; ") & "}")
end if
if (kindParentColl_L contains thisCollKind_S) and ({"album", "smart album"} does not contain thisCollKind_S) then
if debugLogEnable then my loqThis(2, false, "Searching for the collection")
set ctrCollName to (InitCount + 0)
tell application "Capture One 12" to tell thisCollRef to set collSearchSuccess to ({} = (every collection whose name is nbrTargetCollName))
repeat until collSearchSuccess
tell application "Capture One 12" to tell thisCollRef to set refFoundSaColl to (every collection whose name is nbrTargetCollName and kind is smart album)
if 0 < (count of refFoundSaColl) then
tell application "Capture One 12" to set foundSaRules to (get (rules of (item 1 of refFoundSaColl)) as text)
if foundSaRules contains ((item 1 of saRuleCriteriaL) & nbrTargetCollName & (item 2 of saRuleCriteriaL)) then
set {refFoundColl, collSearchMatch} to {(item 1 of refFoundSaColl), true}
exit repeat -- found a matching collection
end if
end if
if (MaxCount < (ctrCollName + 1)) or not enableCounting then
set {collSearchFail, collSearchSuccess} to {true, false}
exit repeat
end if
set ctrCollName to ctrCollName + 1
set nbrTargetCollName to nameTargetColl & separatorChar & ctrCollName
tell application "Capture One 12" to tell thisCollRef to set collSearchSuccess to ({} = (every collection whose name is nbrTargetCollName))
end repeat
if collSearchFail then exit repeat
if enableCreate and collSearchSuccess then
if debugLogEnable then my loqThis(2, false, "Attempting to Create the collection "" & nbrTargetCollName & """)
set savRule to (item 1 of saCreateRuleL) & nbrTargetCollName & (item 2 of saCreateRuleL)
tell application "Capture One 12" to tell thisCollRef to set {refFoundColl, collCreated} to {make new collection with properties {kind:smart album, name:nbrTargetCollName, rules:savRule}, true}
if debugLogEnable and (0 < ctrCollName) then set debugText to "smart album "" & nbrTargetCollName & "" in " & thisCollKind_S & " "" & thisCollName & """
end if
end if
if collCreated or collSearchMatch then
set {hasFoundColl, refParentColl} to {true, thisCollRef}
if debugLogEnable and collCreated then my loqThis(2, false, "Created " & debugText)
if debugLogEnable and collSearchMatch then my loqThis(2, false, "Found " & debugText)
exit repeat
end if
end repeat
if (not hasFoundColl) and enableFindParents and (not collSearchFail) then set {refFoundColl, hasFoundColl, refParentColl} to my findSvaCollection((my findParentColl(thisCollRef)), nameTargetColl, enableCreate, false, enableCounting, InitCount, MaxCount, separatorChar, kindParentColl_L, saCreateRuleL, saRuleCriteriaL)
if not hasFoundColl then my loqThis(0, false, "Could not find the smart album "" & nbrTargetCollName & """)
return {refFoundColl, hasFoundColl, refParentColl}
end findSvaCollection
on findParentColl(thisCollRef)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to find the parent of a collection
## March 23 2019
global debugLogEnable
local errorText, errorText1, parentStringList, refPtr, docPtr, docName, parentPtr, parentRefList, startPtr, stopPtr
if debugLogEnable then my loqThis(2, false, "Starting Parent Search")
try
get || of {thisCollRef}
on error errorText
if debugLogEnable then my loqThis(5, false, "Full error text "" & errorText & """)
end try
## Extract the string between "{" and "}"
repeat with startPtr from 1 to count of errorText
if "{" = text startPtr of errorText then exit repeat
end repeat
repeat with stopPtr from -1 to -(count of errorText) by -1
if "}" = text stopPtr of errorText then exit repeat
end repeat
set errorText to my removeLeadingTrailingSpaces((text (startPtr + 1) thru (stopPtr - 1) of errorText))
## if script runs as an application "collection" is replaced by "«class COcl»", fix that
if "«class COcl»" = text 1 thru 12 of errorText then set errorText to my replaceText(errorText, "«class COcl»", "collection")
set parentStringList to my splitstringtolist(errorText, "of") -- make a list of references
if debugLogEnable then my loqThis(4, false, "Processed error text "" & errorText & """)
repeat with docPtr from (count of parentStringList) to 0 by -1
try
if "document" = first word of item docPtr of parentStringList then exit repeat
end try
end repeat
set {docPtr, parentRefList} to {(docPtr + 0), {}}
if 0 = docPtr then error my loqqed_Error_Halt5("Didn't find "document" in the string "" & errorText & """)
set docName to my removeLeadingTrailingSpaces((get item 2 of my splitstringtolist((my removeLeadingTrailingSpaces((get item docPtr of parentStringList))), """)))
tell application "Capture One 12" to copy (document docName) to beginning of parentRefList
if debugLogEnable then my loqThis(5, false, ("Found Document Reference "" & (get parentStringList's item docPtr) & """))
repeat with parentPtr from 1 to docPtr
if "collection" = (first word of item parentPtr of parentStringList) then exit repeat
end repeat
set parentPtr to parentPtr + 1
if (parentPtr > docPtr) then error my loqqed_Error_Halt5("Unable to find starting collection")
repeat with refPtr from docPtr - 1 to parentPtr by -1
if debugLogEnable then my loqThis(5, false, ("Collection Reference "" & (get parentStringList's item refPtr) & """))
tell application "Capture One 12" to tell (first item of parentRefList) to copy (collection id (get last word of parentStringList's item refPtr)) to beginning of parentRefList
end repeat
return parentRefList
end findParentColl
on getHelp()
return "In this script's Dialog: " & return & ¬
""xxxx" adds the selected variants to the smart album "SVA_xxxx"" & return & ¬
"Shortcuts are: "+","-","<",">"" & return & ¬
"">" adds clones of selected variants" & return & ¬
""<" adds clones of selected variants with adjustments reset" & return & ¬
""+" sets the SVA smart album using the title of the current collection:" & return & ¬
"If the current collection is not an SVA, then the SVA will be "SVA_thisCollectionName"" & return & ¬
"If the current collection is an SVA with no number then the SVA will be "SVA_thisCollectionName_1"" & return & ¬
"If the current collection is an SVA with a number then the number of the new SVA will be incremented" & return & return & ¬
""#nnn" adds the variants to an SVA ending with "_nnn"" & return & ¬
""+xxx"adds the variants to an SVA ending with "xxx"" & return & ¬
"--- n are numbers, x are letters" & return & return & ¬
return & ¬
""-" removes selected variants instead of adding" & return & ¬
"The short cut "->" selects the removed variants in the parent project." & return
end getHelp
-
Part 2.
Copy and paste this code at the end of the code of part 1.[/code]
##########################
## Capture One General Handlers Version 2019/03/11
on validateCOP5(minCOPversionstr, maxCOPversionstr)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose initialisation handler for scripts using Capture One Pro
## Extract and check basic information about the Capture One application
global debugLogEnable
local COPProcList, theAppRef, numCOPversion, minCOPversion, maxCOPversion
local digit_mult, Version_digit, min_digit, max_digit, copVersionStr
local theAppName, copVersion
tell application "System Events"
if debugLogEnable then
my loqThis(2, false, ("COP Processes:" & (get my joinlisttostring((get name of every process whose name begins with "Capture One" and background only is false), ", "))))
my loqThis(3, false, ("All Processes: " & (get my joinlisttostring((get name of every process whose background only is false), ", "))))
end if
set COPProcList to every process whose name begins with "Capture One" and background only is false
if (count of COPProcList) = 0 then return {hasErrors:true, errorText:(get my loqqed_Error_Halt5("COP is not running"))}
if (count of COPProcList) ≠1 then return {hasErrors:true, errorText:(get my loqqed_Error_Halt5("Unexpected: >1 COP instances"))}
set theAppRef to item 1 of COPProcList
set theAppName to ((get name of theAppRef) as text)
set copDetailedVersion to get version of my application theAppName
end tell
tell application "Capture One 12" to set copVersionStr to (get app version)
set copVersion to (word -1 of copVersionStr)
if debugLogEnable then
my loqThis(2, false, ("theAppName: " & theAppName))
my loqThis(1, false, copVersionStr)
my loqThis(2, false, ("Capture One full Version " & copDetailedVersion))
end if
tell my compareVersion(copVersion, minCOPversionstr, maxCOPversionstr) to set {minVersionPass, maxVersionPass} to {its minVersionPass, its maxVersionPass}
if not minVersionPass then return {hasErrors:true, errorText:(get my loqqed_Error_Halt5(("This Script does not support Capture One " & copDetailedVersion & " - supported versions are from " & minCOPversionstr & " to " & maxCOPversionstr)))}
if not maxVersionPass then my loqThis(0, true, ("Caution: Capture One " & copDetailedVersion & " has not been verified yet"))
return {hasErrors:false, theAppName:theAppName, copVersion:copVersion}
end validateCOP5
on validateCOPdoc5(theDocRef, validDocKindList)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose initialisation handler for scripts using Capture One Pro
## Extract and check basic information about a document
global debugLogEnable
local COPDocKind_s, COPDocKind_p, COPDocName
if "text" = (get class of theDocRef as text) and (0 = (get count of theDocRef)) then tell application "Capture One 12" to set theDocRef to get current document
try
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
on error errorText number errorNumber
return {hasErrors:true, errorText:(get my loqqed_Error_Halt5("The Script could not retrieve Capture One document info. Error " & errorNumber & ": "" & errorText & """))}
end try
set COPDocKind_s to convertKindList(COPDocKind_p)
if validDocKindList does not contain COPDocKind_s then return {hasErrors:true, errorText:(get my loqqed_Error_Halt5((COPDocName & " is a " & COPDocKind_s & " -- not supported by this script")))}
return {hasErrors:false, COPDocName:COPDocName, COPDocKind_s:COPDocKind_s}
end validateCOPdoc5
on validateCOPcollections5(theDocRef)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose initialisation handler for scripts using Capture One Pro
## Extract basic information regarding the current collection, and the top level collections
global debugLogEnable
local namesTopCollections, kindsTopCollections_s, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
set COPDocKind_s to convertKindList(COPDocKind_p)
tell application "Capture One 12" to tell theDocRef
set selectedCollectionRef to get current collection
if (missing value = selectedCollectionRef) then
try
set current collection to collection "All Images"
on error
set current collection to last collection
end try
set selectedCollectionRef to get current collection
end if
tell selectedCollectionRef to set {nameSelectedCollection, kindSelectedCollection_s} to {name, my convertKindList(kind)}
set {namesTopCollections, kindsTopCollections_s} to {every collection's name, my convertKindList(every collection's kind)}
end tell
set countTopCollections to count of namesTopCollections
repeat with collectionCounter from 1 to countTopCollections
if (nameSelectedCollection = item collectionCounter of namesTopCollections) and ¬
(kindSelectedCollection_s = item collectionCounter of kindsTopCollections_s) then
set selectedCollectionIndex to collectionCounter
exit repeat
end if
end repeat
local selectedCollectionMirroredAtTopLast, bottomUserCollectionIndex, topUserCollectionIndex, countFavoriteCollections, namesFavoriteCollections
if COPDocKind_s = "catalog" then
repeat with collectionCounter from countTopCollections to 1 by -1
if ("in Catalog" = item collectionCounter of namesTopCollections) and ¬
("smart album" = item collectionCounter of kindsTopCollections_s) then
set topUserCollectionIndex to collectionCounter - 1
exit repeat
end if
end repeat
repeat with collectionCounter from 1 to countTopCollections
if ("Trash" = item collectionCounter of namesTopCollections) and ¬
("smart album" = item collectionCounter of kindsTopCollections_s) then
set bottomUserCollectionIndex to collectionCounter + 1
exit repeat
end if
end repeat
set selectedCollectionMirroredAtTopLast to ¬
(selectedCollectionIndex = countTopCollections) and ¬
({"catalog folder", "favorite"} does not contain last item of kindsTopCollections_s)
set {countFavoriteCollections, namesFavoriteCollections} to {missing value, missing value}
else if COPDocKind_s = "session" then
repeat with collectionCounter from countTopCollections to 1 by -1
if ("favorite" ≠item collectionCounter of kindsTopCollections_s) then
set topUserCollectionIndex to collectionCounter
exit repeat
end if
end repeat
repeat with collectionCounter from 1 to countTopCollections
if ("Trash" = item collectionCounter of namesTopCollections) and ¬
("favorite" = item collectionCounter of kindsTopCollections_s) then
set bottomUserCollectionIndex to collectionCounter + 1
exit repeat
end if
end repeat
set countFavoriteCollections to countTopCollections - topUserCollectionIndex
if 0 = countFavoriteCollections then
set namesFavoriteCollections to {}
else
set namesFavoriteCollections to (get items (topUserCollectionIndex + 1) thru countTopCollections of namesTopCollections)
end if
set selectedCollectionMirroredAtTopLast to false
end if
local selectedCollectionIsUser, namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections
set selectedCollectionIsUser to ¬
(selectedCollectionMirroredAtTopLast or ¬
((selectedCollectionIndex ≥ bottomUserCollectionIndex) and (selectedCollectionIndex ≤ topUserCollectionIndex)))
if topUserCollectionIndex < bottomUserCollectionIndex then
set {topUserCollectionIndex, bottomUserCollectionIndex} to {missing value, missing value}
set {namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections} to {{}, {}, 0}
else
set {namesTopUserCollections, kindsTopUserCollections_s} to {(get items bottomUserCollectionIndex thru topUserCollectionIndex of namesTopCollections), (get items bottomUserCollectionIndex thru topUserCollectionIndex of kindsTopCollections_s)}
set countTopUserCollections to count of namesTopUserCollections
end if
return {hasErrors:false, namesTopUserCollections:namesTopUserCollections, kindsTopUserCollections_s:kindsTopUserCollections_s, countTopUserCollections:countTopUserCollections, selectedCollectionRef:selectedCollectionRef, selectedCollectionIndex:selectedCollectionIndex, kindSelectedCollection_s:kindSelectedCollection_s, nameSelectedCollection:nameSelectedCollection, selectedCollectionMirroredAtTopLast:selectedCollectionMirroredAtTopLast, selectedCollectionIsUser:selectedCollectionIsUser, bottomUserCollectionIndex:bottomUserCollectionIndex, topUserCollectionIndex:topUserCollectionIndex, countFavoriteCollections:countFavoriteCollections, namesFavoriteCollections:namesFavoriteCollections}
end validateCOPcollections5
on convertKindList(kind_list)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General Purpose Handler for scripts using Capture One Pro
## Capture One returns the chevron form of the "kind" property when AppleScript is run as an Application
## Unless care is taken to avoid text conversion of this property, this bug breaks script decisions based on "kind"
## This script converts text strings with the chevron form to strings with the expected text form
## The input may be a single string, a single enum, a list of strings or a list of enums
## The code is not compact but runs very fast, between 60us and 210us per item
local kind_s_list, input_is_list, theItem, kind_s1, fail_flag, code_start, kind_list, Kind_s_item, kind_code, kind_type
set kind_s_list to {}
set input_is_list to ("list" = (get (class of kind_list) as text))
if input_is_list then
if ("text" = (get (class of item 1 of kind_list) as text)) and ¬
("«" ≠(get text 1 of item 1 of kind_list)) then return kind_list -- quick pass through if first item is OK
repeat with theItem in kind_list
tell application "Capture One 12" to set the end of kind_s_list to (get theItem as text)
end repeat
if ("«" ≠(get text 1 of item 1 of kind_s_list)) then return kind_s_list
else
if ("text" = (get (class of kind_list) as text)) and ¬
("«" ≠(get text 1 of kind_list)) then return kind_list -- quick pass through if first item is OK
tell application "Capture One 12" to set kind_s1 to (get kind_list as text)
if "«" ≠(get text 1 of kind_s1) then return kind_s1 -- quick pass through if input is OK
set kind_s_list to {kind_s1}
end if
set fail_flag to false
set code_start to -5
set kind_list to {}
repeat with Kind_s_item in kind_s_list
if ("«" ≠(get text 1 of Kind_s_item)) or (16 > (count of Kind_s_item)) then ¬
error (get my loqqed_Error_Halt5("convertKindList received an unexpected Kind string: " & Kind_s_item))
set kind_code to get (text code_start thru (code_start + 3) of Kind_s_item)
set kind_type to get (text code_start thru (code_start + 1) of Kind_s_item)
if kind_type = "CC" then ## Collection Kinds
if kind_code = "CCpj" then
set the end of kind_list to "project"
else if kind_code = "CCgp" then
set the end of kind_list to "group"
else if kind_code = "CCal" then
set the end of kind_list to "album"
else if kind_code = "CCsm" then
set the end of kind_list to "smart album"
else if kind_code = "CCfv" then
set the end of kind_list to "favorite"
else if kind_code = "CCff" then
set the end of kind_list to "catalog folder"
else
set fail_flag to true
end if
else if kind_type = "CL" then ## Layer Kinds
if kind_code = "CLbg" then
set the end of kind_list to "background"
else if kind_code = "CLnm" then
set the end of kind_list to "adjustment"
else if kind_code = "CLcl" then
set the end of kind_list to "clone"
else if kind_code = "CLhl" then
set the end of kind_list to "heal"
else
set fail_flag to true
end if
else if kind_type = "CR" then ## Watermark Kinds
if kind_code = "CRWn" then
set the end of kind_list to "none"
else if kind_code = "CRWt" then
set the end of kind_list to "textual"
else if kind_code = "CRWi" then
set the end of kind_list to "imagery"
else
set fail_flag to true
end if
else if kind_type = "CO" then ## Document Kinds
if kind_code = "COct" then
set the end of kind_list to "catalog"
else if kind_code = "COsd" then
set the end of kind_list to "session"
else
set fail_flag to true
end if
else
set fail_flag to true
end if
if fail_flag then ¬
error (get my loqqed_Error_Halt5("convertKindList received an unexpected Kind string: " & Kind_s_item))
end repeat
if input_is_list then
return kind_list
else
return item 1 of kind_list
end if
end convertKindList
## my joinListToString
## Uncomment these next lines if to use these handlers as a Script Library file (".scptd")
(*
use AppleScript version "2.5"
use scripting additions
use U : script "Utilities_1215"
if false then false
*)
#########################
## Logging Handlers Version 2019/02/23
on setupLoqqing5()
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## Handler to setup the variables used by the loqqing system at the start of the script
global debugLogEnable, Loqqing, loqResultDocRef, loqResultMethod, loqDialogTextList
## loqResultDocRef is kept separate from Loqqing to allow easy listing of Loqqing
## Default values are entered
set {loqResultDocRef, loqResultMethod} to {false, "not Initialized"}
set Loqqing to {stateResultDoc:false, gateResultsFile:false, enableResultsFile:false, initResultDoc:false, ResultsFileMaxDebug:0, debugLogLevel:0}
set Loqqing to Loqqing & {stateResultsByClipboard:true, gateResultsByClipboard:true, enableResultsByClipboard:true}
set Loqqing to Loqqing & {stateResultsByDialog:false, gateResultsDialog:false, enableResultsByDialog:false, maxDialogPercent:50, maxDialogLines:25, maxDialogChar:1000}
set Loqqing to Loqqing & {stateResultsByNotification:false, gateResultsNotification:false, enableResultsByNotifications:false, enableNotifications:true, notificationsMaxDebug:0}
set Loqqing to Loqqing & {stateResultsByLoq:true, gateParentLoqqing:true, enableResultsByLoq:true, initLoqqing:false, gateLoqqing:true}
end setupLoqqing5
on InitializeLoqqing5(DocName_Ext, sourceTitle)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## Handler to initialize logging of results
## Do use loq_Results() until the end of the handler and initialising is completed
## Doesn't set loqResultMethod, this is set by caller of this handler
global parent_name, Script_Title, Loqqing, gateScriptProgress, loqDialogTextList, loqResultDocRef
local LogMethods, LogHeader, date_string
tell current application to set date_string to (current date) as text
set LogMethods to {}
set LogHeader to (sourceTitle & " results on " & date_string)
local targetFileWasCreated, TextEditlist, createdLine
if Loqqing's gateResultsFile and Loqqing's enableResultsFile then
set end of LogMethods to DocName_Ext
if not Loqqing's initResultDoc then
set targetFileWasCreated to false
set Loqqing's stateResultDoc to false
set Loqqing's initResultDoc to true
## If TextEdit is already open and has the document open then add the headerline
tell application "System Events" to set TextEditlist to get background only of every application process whose name is "TextEdit"
if (0 < (count of TextEditlist)) and not item 1 of TextEditlist then
if (DocName_Ext is in (get name of documents of application "TextEdit")) then
tell application "TextEdit" to tell document DocName_Ext
set loqResultDocRef to it
tell its text to set paragraph (1 + (count paragraphs)) to return & LogHeader & return
set Loqqing's stateResultDoc to true
end tell
end if
end if
end if
local targetFolderParent_a, targetFolderParent_p, targetFolderName, targetFolder_a, targetFolder_p, ResultDocPath_a, ResultDocPath_p, newFolderRef, newFileRef
if not Loqqing's stateResultDoc or (false = loqResultDocRef) then
-- create the document and the folder if necessary
-- Do not use finder to test for the file existence because it has a bug that ignores leading 0's
-- https://www.macscripter.net/viewtopic.php?id=45178
set targetFolderParent_a to alias (get path to desktop folder as text)
set targetFolderParent_p to get POSIX path of targetFolderParent_a
set targetFolderName to "ScriptReports"
set targetFolder_p to (targetFolderParent_p & targetFolderName)
set ResultDocPath_p to targetFolder_p & "/" & DocName_Ext
try
set ResultDocPath_a to (get alias POSIX file ResultDocPath_p)
on error
try
set targetFolder_a to (get alias POSIX file targetFolder_p) --x1
on error
tell application "Finder" to set newFolderRef to make new folder at targetFolderParent_a with properties {name:targetFolderName}
set targetFolder_a to newFolderRef as alias
end try
tell application "Finder" to set newFileRef to make new file at targetFolder_a with properties {name:DocName_Ext}
set ResultDocPath_a to newFileRef as alias
set targetFileWasCreated to true
end try
set createdLine to ("Created by " & Script_Title & " on " & date_string)
tell application "TextEdit" -- open the document and add the first line if empty
activate
set loqResultDocRef to open ResultDocPath_a
tell text of loqResultDocRef
if targetFileWasCreated then
set paragraph 1 to createdLine & return & return
else
if (0 = (count of paragraphs)) then set paragraph 1 to createdLine & return & return
end if
end tell
if 2 ≤ Loqqing's debugLogLevel then tell me to log ResultDocPath_p & ": " & createdLine
end tell
set Loqqing's stateResultDoc to true -- prevents initialisation from repeating
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to return & LogHeader & return
end if
end if
if Loqqing's gateResultsFile and Loqqing's stateResultDoc and not Loqqing's enableResultsFile then
set Loqqing's stateResultDoc to false
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to return & parent_name & "Results reporting disabled for: " & Script_Title & return
end if
local screenWidthO, screenHeightO, screenWidth, screenHeight, fontSize_pts, charactersPerLine, dotsPer_Point, linesPerScreen, borderLines
if Loqqing's gateResultsDialog and Loqqing's enableResultsByDialog then
set end of LogMethods to "Dialogs"
if not Loqqing's stateResultsByDialog then
set loqDialogTextList to (get LogHeader & return)
set Loqqing's stateResultsByDialog to true
end if
tell application "Finder" to set {screenWidthO, screenHeightO, screenWidth, screenHeight} to bounds of window of desktop
set fontSize_pts to 12 -- estimated font size for display dialog, including line spacing
set charactersPerLine to 58 -- estimated characters per line fo display dialog, if there are no "return" characters
set dotsPer_Point to 1.5 -- similar for 5K 27" iMac and 11" MBA - similar for others - retina independent
set linesPerScreen to (screenHeight - screenHeightO) / (fontSize_pts * dotsPer_Point)
set borderLines to 5
set Loqqing's maxDialogLines to (get ((Loqqing's maxDialogPercent) / 100 * (linesPerScreen - borderLines)) as integer)
set Loqqing's maxDialogChar to (get ((Loqqing's maxDialogPercent) / 100 * (linesPerScreen - borderLines) * charactersPerLine) as integer)
if 2 ≤ Loqqing's debugLogLevel then log {"maxDialogPercent", Loqqing's maxDialogPercent, "screenHeight", screenHeight, "linesPerScreen", linesPerScreen, "maxDialogLines", Loqqing's maxDialogLines, "maxDialogChar", Loqqing's maxDialogChar}
end if
if Loqqing's gateResultsDialog and Loqqing's stateResultsByDialog and not Loqqing's enableResultsByDialog then
set Loqqing's stateResultsByDialog to false
set loqDialogTextList to ""
end if
if Loqqing's gateResultsByClipboard and Loqqing's enableResultsByClipboard then
set end of LogMethods to "Clipboard"
if not Loqqing's stateResultsByClipboard then
set the clipboard to LogHeader
set Loqqing's stateResultsByClipboard to true
end if
end if
if Loqqing's gateResultsByClipboard and Loqqing's stateResultsByClipboard and not Loqqing's enableResultsByClipboard then
set the clipboard to {}
set Loqqing's stateResultsByClipboard to false
end if
global gateScriptProgress
if not Loqqing's initLoqqing then
set Loqqing's initLoqqing to true
if ("Script Editor" = parent_name) then
set Loqqing's gateParentLoqqing to true
set Loqqing's gateLoqqing to true
set gateScriptProgress to true
try -- Open the Log History window
tell application "System Events" to tell application process "Script Editor"
if (get name of windows) does not contain "log History" then ¬
click menu item "Log History" of menu "Window" of menu bar 1
end tell
end try
else if ("Script Debugger" = parent_name) then
set Loqqing's gateParentLoqqing to true
set Loqqing's gateLoqqing to true
set gateScriptProgress to true
## Avoids compiler errors when Script Debugger is not present
run script "tell application "Script Debugger" to tell first document to set event log visible to true"
run script "tell application "Script Debugger" to tell first document to set event log scope bar visible to true"
else
set Loqqing's gateParentLoqqing to false
set Loqqing's gateLoqqing to false
set gateScriptProgress to false
end if
end if
if Loqqing's gateParentLoqqing and Loqqing's enableResultsByLoq then
set end of LogMethods to " " & parent_name & " Log"
if not Loqqing's stateResultsByLoq then
set Loqqing's stateResultsByLoq to true
log "Results Logging enabled for: " & Script_Title
end if
end if
if Loqqing's gateParentLoqqing and Loqqing's stateResultsByLoq and not Loqqing's enableResultsByLoq then
set Loqqing's stateResultsByLoq to false
log "Results Logging disabled for: " & Script_Title
end if
if (Loqqing's gateResultsNotification and Loqqing's enableResultsByNotifications) or Loqqing's enableNotifications then
set end of LogMethods to "Notifications"
if not Loqqing's stateResultsByNotification then
--display notification "Notifications enabled for: " & Script_Title
set Loqqing's stateResultsByNotification to true
end if
else if Loqqing's stateResultsByNotification then
display notification "Notifications disabled for: " & Script_Title
set Loqqing's stateResultsByNotification to false
end if
set LogMethods_S to my joinlisttostring(LogMethods, ", ")
if 2 ≤ Loqqing's debugLogLevel then loqThis(2, false, ("Result Reported by " & LogMethods_S))
return LogMethods_S
end InitializeLoqqing5
on loqThis(thisLogDebugLevel, MakeFront, log_Text)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler for logging results
## log results if the debug level of the message is below the the threshold set by debugLogLevel
## log the results by whatever mechanism is ebabled - {Script Editor Log, Text Editor Log, Display Dialog}
global parent_name, Loqqing, loqDialogTextList, loqResultDocRef
local log_Text_S
if thisLogDebugLevel ≤ Loqqing's debugLogLevel then
set log_Text_S to my joinlisttostring(log_Text, "; ")
set triggeredDialog to false
if (thisLogDebugLevel = 0) then -- Critical errors
if Loqqing's gateLoqqing then log (log_Text_S)
if Loqqing's enableResultsByClipboard then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
if Loqqing's enableResultsFile then ¬
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to (log_Text_S & return)
if Loqqing's enableResultsByDialog then display alert log_Text_S giving up after 10
(Loqqing's enableResultsByNotifications and (thisLogDebugLevel ≤ 0))
else if (thisLogDebugLevel ≤ 0) then -- Expected Results
if Loqqing's enableResultsByLoq then log (log_Text_S)
if Loqqing's enableResultsByClipboard then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
if Loqqing's enableResultsFile then ¬
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to (log_Text_S & return)
if Loqqing's enableResultsByDialog then -- process the dialog last
set loqDialogTextList to (get loqDialogTextList & return & log_Text_S)
if MakeFront or (0 ≥ Loqqing's maxDialogLines) or ¬
(Loqqing's maxDialogLines < (get count of paragraphs of loqDialogTextList)) or ¬
(Loqqing's maxDialogChar < (get length of loqDialogTextList)) then
--tell application "System Events" to set frontmost of process parent_name to true
display dialog loqDialogTextList
set triggeredDialog to true
set loqDialogTextList to ""
end if
end if
else -- debugging information
if Loqqing's gateLoqqing then log (log_Text_S)
if Loqqing's enableResultsFile and ((thisLogDebugLevel ≤ Loqqing's ResultsFileMaxDebug) or not Loqqing's gateLoqqing) then ¬
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to ((log_Text_S as text) & return)
end if
if (Loqqing's enableNotifications and (thisLogDebugLevel ≤ Loqqing's notificationsMaxDebug)) or ¬
(Loqqing's enableResultsByNotifications and (thisLogDebugLevel ≤ 0)) then
set paraCtr to 0
set notString to ""
set lineCnt to 39
set paramax to 3
copy (get paramax * lineCnt) to remChar
repeat with thePara in (get paragraphs of log_Text_S)
set thisCount to (get count of (get contents of thePara))
if thisCount > 0 then
set paraCtr to paraCtr + 1
if thisCount > remChar then copy remChar to thisCount
set notString to notString & (get text 1 thru thisCount of thePara)
set remChar to remChar - lineCnt * (thisCount div lineCnt)
if (0 < (thisCount mod lineCnt)) then set remChar to remChar - lineCnt
if (paramax ≤ paraCtr) or (0 ≥ remChar) then exit repeat
set notString to notString & return
end if
end repeat
display notification notString
end if
if (not triggeredDialog) and (MakeFront or (0 = thisLogDebugLevel)) then --
if Loqqing's gateLoqqing then tell application "System Events" to set frontmost of process parent_name to true
if Loqqing's enableResultsFile then tell application "System Events" to set frontmost of process "TextEdit" to true
end if
else
set log_Text_S to ""
end if
return log_Text_S
end loqThis
on loqqed_Error_Halt5(errorText)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler for logging during script termination
global debugLogEnable, Script_Title
tell current application to set date_string to (current date) as text
return (get (loqThis(0, true, ("Script "" & Script_Title & "" is exitting at " & date_string))) & (loqThis(0, true, ("Exit Reason: " & errorText & return))))
end loqqed_Error_Halt5
#########################
## General Utility Handlers Version 2019/01/12
on compareVersion(testVersion_S, minVersion_S, maxVersion_S)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler for comparing versions
--local digitMult, testVersionNumber, minVersionNumber, maxVersionNumber, testVersion_LS, minVersion_LS, maxVersion_LS, groupsCount, group_ctr
--local testGroupsCount, minGroupsCount, maxGroupsCount, hasTestGroup, hasMinGroup, hasMaxGroup, testGroupVersion, minGroupVersion, maxGroupVersion
--local allBoundsPass, lowerBoundPass, upperBoundPass, lowerBoundFail, upperBoundFail
if (text ≠(get class of testVersion_S)) or (text ≠(get class of minVersion_S)) or (text ≠(get class of maxVersion_S)) then error "Text inputs only"
set testVersion_LS to (splitstringtolist(removeLeadingTrailingSpaces(testVersion_S), "."))
set minVersion_LS to (splitstringtolist(removeLeadingTrailingSpaces(minVersion_S), "."))
set maxVersion_LS to (splitstringtolist(removeLeadingTrailingSpaces(maxVersion_S), "."))
set testGroupsCount to get count of testVersion_LS
set minGroupsCount to get count of minVersion_LS
set maxGroupsCount to get count of maxVersion_LS
set groupsCount to testGroupsCount
if groupsCount < minGroupsCount then set groupsCount to minGroupsCount
if groupsCount < maxGroupsCount then set groupsCount to maxGroupsCount
set lowerBoundPass to false
set lowerBoundFail to false
set upperBoundPass to false
set upperBoundFail to false
repeat with group_ctr from 1 to groupsCount
set hasTestGroup to (get group_ctr ≤ testGroupsCount)
set hasMinGroup to (get group_ctr ≤ minGroupsCount)
set hasMaxGroup to (get group_ctr ≤ maxGroupsCount)
if hasTestGroup then set testGroupVersion to (get (item group_ctr of testVersion_LS) as integer)
if hasMinGroup then set minGroupVersion to (get (item group_ctr of minVersion_LS) as integer)
if hasMaxGroup then set maxGroupVersion to (get (item group_ctr of maxVersion_LS) as integer)
if not (lowerBoundPass or lowerBoundFail) then
if hasMinGroup and hasTestGroup then
if testGroupVersion > minGroupVersion then set lowerBoundPass to true
if testGroupVersion < minGroupVersion then set lowerBoundFail to true
else if hasMinGroup then -- (and no testGroup) e.g. test 11 , min 11.1
set lowerBoundFail to true -- not symetric with upper bound behaviour
else if hasTestGroup then -- (and no minGroup) e.g. test 11.1, min 11 --> OK
set lowerBoundPass to true
end if
end if
if not (upperBoundPass or upperBoundFail) then
if hasMaxGroup and hasTestGroup then
if testGroupVersion < maxGroupVersion then set upperBoundPass to true
if testGroupVersion > maxGroupVersion then set upperBoundFail to true
else if hasMaxGroup then -- (and no testGroup) e.g. test 11.1 , max 11.1.2 or test 11 , max 11.1
set upperBoundPass to true -- not symetric with lower bound behaviour
else if hasTestGroup then --(and no maxGroup) e.g. test 11.1, max 11 --> pass
set upperBoundPass to true
end if
end if
end repeat
return {maxVersionPass:(not upperBoundFail), minVersionPass:(not lowerBoundFail)}
end compareVersion
on splitstringtolist(theString, theDelim)
## Public Domain
set theList to {}
set astid to AppleScript's text item delimiters
try
set AppleScript's text item delimiters to theDelim
set theList to text items of theString
on error
set AppleScript's text item delimiters to astid
end try
set AppleScript's text item delimiters to astid
return theList
end splitstringtolist
on joinlisttostring(theList, theDelim)
## Public Domain
set theString to ""
set astid to AppleScript's text item delimiters
try
set AppleScript's text item delimiters to theDelim
set theString to theList as string
end try
set AppleScript's text item delimiters to astid
return theString
end joinlisttostring
on removeLeadingTrailingSpaces(theListString)
## 40% faster than a version which trims the string 1 space at a time
## handles both string and list input correctly
-- local theListString, input_is_list, cleanList, thecount, hasTriggered, indexLow, indexHigh
set input_is_list to ("list" = (get (class of theListString) as text))
if not input_is_list then set theListString to {theListString}
set cleanList to {}
repeat with theString in theListString
if ("text" = (get (class of theString) as text)) then
set thecount to (get count of theString)
set hasTriggered to false
repeat with indexLow from 1 to thecount
if " " ≠(get text indexLow of theString as text) then
set hasTriggered to true
exit repeat
end if
end repeat
if not hasTriggered then
set theString to ""
else
repeat with indexHigh from -1 to (-thecount) by -1
if " " ≠(get text indexHigh of theString as text) then exit repeat
end repeat
set theString to text indexLow thru indexHigh of theString
end if
end if
set the end of cleanList to theString
end repeat
if input_is_list then return cleanList
return (get contents of theString)
end removeLeadingTrailingSpaces
on removeLeadingTrailingSpaces2(theListString, trimLeading, trimTrailing)
## 40% faster than a version which trims the string 1 space at a time
## handles both string and list input correctly
local theListString, input_is_list, cleanList, thecount, foundChar, isAllBlanks, indexLow, indexHigh
set input_is_list to ("list" = (get (class of theListString) as text))
if not input_is_list then set theListString to {theListString}
set cleanList to {}
repeat with theString in theListString
if ("text" = (get (class of theString) as text)) then
set {thecount, indexLow, indexHigh, isAllBlanks} to {(get count of theString), 1, -1, false}
if trimLeading then
set foundChar to false
repeat with indexLow from 1 to thecount
if " " ≠(get text indexLow of theString as text) then
set foundChar to true
exit repeat
end if
end repeat
set isAllBlanks to not foundChar
end if
if trimTrailing and not isAllBlanks then
set foundChar to false
repeat with indexHigh from -1 to (-thecount) by -1
if " " ≠(get text indexHigh of theString as text) then
set foundChar to true
exit repeat
end if
end repeat
set isAllBlanks to not foundChar
end if
if isAllBlanks then
set theString to ""
else
set theString to text indexLow thru indexHigh of theString
end if
end if
set the end of cleanList to theString
end repeat
if input_is_list then return cleanList
return (get contents of theString)
end removeLeadingTrailingSpaces2
on removeTextFromList(theList, theBadChar)
## theBadChar may be a string or character, whereupon that string will be removed
## theBadChar may be a list of strings or characters, whereupon those strings will be removed
set theDelim to character id 60000 -- obscure character chosen for the low likelihood of its appearance
set astid to AppleScript's text item delimiters
try
set AppleScript's text item delimiters to theDelim
set theString to theList as string
set AppleScript's text item delimiters to theBadChar
set text_list to every text item of theString
set AppleScript's text item delimiters to ""
set cleanText_S to the text_list as string
set AppleScript's text item delimiters to theDelim
set theList to text items of cleanText_S
end try
set AppleScript's text item delimiters to astid
return theList
end removeTextFromList
on removeItemFromList(theList, theBadItem)
## theBadChar may be a string or character, whereupon that string will be removed
## theBadChar may be a list of strings or characters, whereupon those strings will be removed
local theDelim, theString, text_list, cleanText_S, errorText, astid
set theDelim1 to character id 60000 -- obscure characters chosen for the low likelihood of its appearance
set theDelim2 to character id 60001
set astid to AppleScript's text item delimiters
try
set AppleScript's text item delimiters to {theDelim2 & theDelim1}
set theString to theDelim1 & (theList as string) & theDelim2
set AppleScript's text item delimiters to theDelim1 & theBadItem & theDelim2
set text_list to every text item of theString
set AppleScript's text item delimiters to ""
set cleanText_S to text_list as string
if 2 < (count of cleanText_S) then
set cleanText_S to text 2 thru -2 of (text_list as string)
else
set cleanText_S to ""
end if
set AppleScript's text item delimiters to {theDelim2 & theDelim1}
set theList to text items of cleanText_S
end try
set AppleScript's text item delimiters to astid
return theList
end removeItemFromList
on replaceText(this_text, search_string, replacement_string)
set astid to AppleScript's text item delimiters
try
set AppleScript's text item delimiters to the search_string
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the replacement_string
set this_text to the item_list as string
end try
set AppleScript's text item delimiters to astid
return this_text
end replaceText0
投稿コメントは受け付けていません。
コメント
1件のコメント