content: -- FIND OMNIFOCUS FOLDERS/PROJECTS/TASKS/CONTEXTS WITH REUSABLE QUERIES
  
  property pVersion : "0.739"
  property pTitle : "Where in OmniFocus ? (ver " & pVersion & ")"
  
  property plngMRUHistory : 25 -- Total number of MRU items to record in search history
  property plngShortMRU : 5 -- Number of MRU items to show on Library dialog
  
  property pblnDefaultShortCut : false -- Jump straight to selecting matches in OF GUI, or list query harvest for manual sub-selection ?
  property pblnDefaultNewWindow : false -- Create a new window in which to select matches ?
  property pblnDefaultWarnHidden : true -- Warn if any matches are hidden by filter settings (and offer to relax filters) ?
  property pblnHideWindow : false
  
  -- Find and optionally select OmniFocus tasks, projects, or contexts by entering 
  -- applescript-like command line queries, or choosing them from a list saved in a text file.
  -- The syntax is that of an Applescript WHOSE / WHERE clause, 
  -- (The query should begin with the words "where"/"whose" and tasks / projects / contexts)
  -- Assisted query-building is available, with lists of available properties,
  -- drop-down lists of possible query clauses
  -- and prompting for entry of date, number, and search string values
  -- Queries entered manually or saved in the library may include the place-holders:
  -- 		-- <txt> for strings
  -- 		-- <num> for integers
  -- 		-- <dte> for dates
  -- The query-handler will prompt the user to supply appropriate values, 
  -- and both the "filled" and "template" forms of succesful queries will be stored
  -- in the MRU (most recently used) list.
  -- There are currently two minor extensions to the applescript query language:
  -- - The terms today/yesterday/tomorrow/soon may be used in place of dates.
  -- - Searches may be prefixed by the term 'archived'
  
  -- 	Copyright © 2010, Robin Trew
  --  All rights reserved.
  -- 	
  -- 	Redistribution and use in source and binary forms, with or without modification, 
  -- 	are permitted provided that the following conditions are met:
  -- 	
  -- 		- Redistributions of source code must retain the above copyright notice, 
  -- 		  this list of conditions and the following disclaimer.
  -- 		- Redistributions in binary form must reproduce the above copyright notice, 
  -- 		  this list of conditions and the following disclaimer in the documentation 
  -- 		  and/or other materials provided with the distribution.
  -- 		
  -- 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  -- 	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
  -- 	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
  -- 	IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
  -- 	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  -- 	 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  -- 	 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  -- 	 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  -- 	 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  --  RECENT CHANGE LOG
  
  
  -- Ver 0.702	- Parent code copies properties of classes, rather than getting references to lists (better for memory management ?)
  -- Ver 0.707 - Moved Entry dialog code into sEntry sub-class of sMenu
  -- Ver 0.708	- Prevented reduplication of comment section in default string if Entry dialog is activated after no selections are made in ChooseFromHarvest()
  -- Ver 0.709 - Restored --option section to default string if we return to Entry dialog after zero matches
  -- Ver 0.710 - Corrected a bug which duplicated searches in the MRU list
  -- 				- Patched a bug involving assisted search edit for object properties like container (working on more general improvements here)
  -- Ver 0.712 - Corrected a bug which dropped any "archived" prefix from filled query entries in MRU lists
  -- Ver 0.714 - Fixed a spurious error message which complained when bracket characters had white space on both sides
  -- Ver 0.717 - Introduced timed retries to deal with an intermittent failure to select sidebar projects at times when the application was busy
  -- Ver 0.720 - Fixed a glitch in the available abbrevns listed for tasks in the assisted entry menu - thanks to Bill Palmer for bug report.
  -- ver 0.722 - First draft in which interval strings can be applied to month tags, and to now|yesterday|tomorrow as well as today
  -- ver 0.724 - Fixed a bug affecting queries containing more than one relative date (only the first relative date was interpreted)
  -- ver 0.728 - Corrected handling of repetition and review intervals to allow forms like:
  --			 -  projects where review interval = {unit:day, fixed:false, steps:1} or review interval = {unit:day, fixed:false, steps:2} 
  -- ver 0.730 - corrected handling of <num> etc placeholders in repetition and review interval queries
  -- ver 0.733 - Fix for a focus issue on selection of items in OF
  -- ver 0.737 - Minor changes to dialogs, and removed insistence on ver 1.8
  -- ver 0.739 - Adapted so that copies purchased through the App Store can run it
  
  property pblnLibsInPackage : false --  Search for library files either inside the application package or in the same folder as the app
  -- Having this set to **false** is preferable: it means that you will automatically keep your own query and abbreviation files 
  -- if you download an updated version of this script. The query and abbrevn files will be stored (file names below) in the same folder as the app
  -- If no files of these names exist in the app's folder, fresh copies, with default contents, will automatically be made.
  -- if pblnLibsInPackage is set to true, all edits will be to the files inside the package itself (this is unlikely to be the best approach).
  
  property pLibraryFile : "WhereInOFQueries.txt" -- Folder is returned by ScriptFolder()
  property pAbbrevnFile : "WhereInOFAbbrevns.txt" -- ditto
  
  property plstMonthTags : {"<jan>", "<feb>", "<mar>", "<apr>", "<may>", "<jun>", "<jul>", "<aug>", "<sep>", "<oct>", "<nov>", "<dec>"}
  
  property plstObjects : {} -- used to store objects matching the query
  property plstParents : {} -- used to record parent projects or contexts during task searches
  
  -- and allow persistent storage of changes made to script behaviour options through the script interface:
  global gblnDefaultShortCut
  global gblnDefaultNewWindow
  global gblnDefaultWarnHidden
  
  -- persistence of Most Recently Used query list:
  global glstMRU
  
  on run argv
  	local strQuery, strComments -- sMenu results
  	local blnIsArchive, lngClass, strFilledQuery, strError -- sSearch results
  	local blnInGUI -- sDisplay results
  	
  	set strCmdOPENING_MENU to "- LIBRARY of searches"
  	set strCmdManual to "- ENTER NEW SEARCH"
  	local lstTypeFlags, lstOptions
  	-- EXIT WITH NOTIFICATION IF THE REQUIRED VERSION OF OMNIFOCUS IS NOT RUNNING
  	if not RunningOmniFocus() then return
  	-- IF NECESSARY, INITIALIZE PERSISTENT VARIABLES WHICH HOLD GLOBAL SCRIPT OPTIONS
  	InitializeOptions()
  	
  	-- 	-- 	-- ???? Experiment with command line usage
  	try
  		if class of argv is list then
  			set strQuery to first item of argv
  			tell sSearch
  				QueryMatches(strQuery)
  				set {blnIsArchive, lngClass, strFilledQuery, strError} to {sSearch's spIsArchive, sSearch's spClass, sSearch's spFilledQuery, sSearch's spError}
  			end tell
  			
  			tell sDisplay
  				OFGUIShow(blnIsArchive, strFilledQuery, lngClass, "")
  				set blnInGUI to sDisplay's spIsInGUI
  			end tell
  			return
  		end if
  	end try
  	-- 	-- 	-- ????
  	
  	-- Open Menu {Library, MRU, New Entry, House-keeping}
  	set {strMenu, blnESC, strEntryPrompt, strEntryDraft} to {strCmdOPENING_MENU, false, "", ""}
  	
  	repeat while not blnESC
  		tell sMenu
  			set {sMenu's spStartMenu, sMenu's spEntryPrompt, sMenu's spEntryDraft} to {strMenu, strEntryPrompt, strEntryDraft}
  			MainLoop()
  			set {strQuery, strComments} to {sMenu's spMenuQuery, sMenu's spMenuComments}
  		end tell
  		
  		if length of strQuery < 1 then return
  		
  		-- FETCH OBJECTS MATCHING THE USER'S QUERY (PLACING A COPY OF THE QUERY IN THE CLIPBOARD FOR FURTHER EDITING)
  		tell sSearch
  			QueryMatches(strQuery)
  			set {blnIsArchive, lngClass, strFilledQuery, strError} to {sSearch's spIsArchive, sSearch's spClass, sSearch's spFilledQuery, sSearch's spError}
  		end tell
  		
  		-- Place draft query in clipboard. Usually overriden by query fetched from MRU, but only successful queries
  		-- get into the MRU, so this might occasionally be useful, if the MRU has been cleared.
  		tell application id "com.apple.finder" to set the clipboard to strFilledQuery & strComments
  		
  		-- IN CASE OF ERROR (query language or other) SHOW ERROR MESSAGE, 
  		-- AND ALLOW FURTHER EDITING
  		if length of strError > 0 then
  			set strMenu to strCmdManual
  			set strEntryPrompt to strError
  			set strEntryDraft to strFilledQuery & strComments
  		else
  			-- IN CASE OF SUCCESS: SHOW ANY OBJECTS FOUND AND INVITE MANUAL SELECTION OF SUBSET
  			if (count of plstObjects) > 0 then
  				-- DISPLAY RESULTS (Either immediately in GUI, or as a list for preliminary manual selection)
  				--display dialog lngClass as string
  				tell sDisplay
  					OFGUIShow(blnIsArchive, strFilledQuery, lngClass, strComments)
  					set blnInGUI to sDisplay's spIsInGUI
  				end tell
  				-- SAVE (SUCCESSFUL) QUERY (AND ANY TEMPLATE ON WHICH IT WAS BASED) TO MRU & CLIPBOARD
  				set {strFilledQuery, strQuery} to {strFilledQuery & strComments, strQuery & strComments}
  				if blnIsArchive then set strFilledQuery to "archived " & strFilledQuery
  				SaveToMRU(strFilledQuery, strQuery)
  				
  				-- RETURN TO MENU IF NO MATCHES HAVE BEEN SELECTED IN GUI, OTHERWISE EXIT
  				set strMenu to strCmdOPENING_MENU
  				set strEntryDraft to strFilledQuery
  				set blnESC to blnInGUI
  			else
  				-- LET USER KNOW IF NOTHING FOUND
  				ReportZeroMatches(strFilledQuery, lngClass)
  				-- AND OFFER A CHANCE TO ADJUST THE (FILLED) QUERY
  				set strMenu to strCmdManual
  				set strEntryDraft to strQuery & strComments
  			end if
  		end if
  		ClearHarvest()
  	end repeat
  	ClearHarvest()
  end run
  
  -------------------------------------------------------------------------------------------------------------------------------------
  --- MAIN SCRIPT FUNCTIONS (OTHER SCRIPTS AT END)
  
  on InitializeOptions()
  	try
  		gblnDefaultShortCut
  		gblnDefaultNewWindow
  		gblnDefaultWarnHidden
  	on error
  		set gblnDefaultShortCut to pblnDefaultShortCut
  		set gblnDefaultNewWindow to pblnDefaultNewWindow
  		set gblnDefaultWarnHidden to pblnDefaultWarnHidden
  	end try
  	ClearHarvest()
  end InitializeOptions
  
  on ClearHarvest()
  	set plstObjects to {}
  	set plstParents to {}
  end ClearHarvest
  
  on ParseComments(str)
  	-- Decide whether this comment segment consists of instructions or not
  	-- Break it into a list of {key, value} lists
  	set lstTokens to Tokenize(str)
  	set my text item delimiters to "="
  	set lstKeyValue to {}
  	repeat with oToken in lstTokens
  		set lstParts to text items of oToken
  		set strKey to first item of lstParts
  		set strValue to ""
  		if (count of lstParts) > 1 then set strValue to item 2 of lstParts
  		if length of strValue > 0 then
  			if last character of strValue is "," then set strValue to text 1 thru -2 of strValue -- purge commas
  			if text 1 of strValue is "[" then set strValue to "-" -- normalize [Existing] to "-"
  		end if
  		set end of lstKeyValue to {strKey, strValue}
  	end repeat
  	
  	-- Initialize defaults
  	set blnContext to false
  	set {strSmartGroup, strGrouping, strSorting, strState, strFlagged, strDurn} to {"-", "-", "-", "-", "-", "-"}
  	set {blnShortCut, blnNewWindow, blnWarnHidden} to {gblnDefaultShortCut, gblnDefaultNewWindow, gblnDefaultWarnHidden}
  	
  	-- loop through the potential {key,value} pairs
  	repeat with oKeyValue in lstKeyValue
  		set {strKey, strValue} to oKeyValue
  		if length of strValue > 0 then
  			set strValStart to text 1 of strValue
  		else
  			set strValStart to ""
  		end if
  		
  		if strKey is in {"sh", "ShortCut"} then
  			set blnShortCut to not (strValStart is "n")
  		else if strKey is in {"nw", "wn", "NewWindow"} then
  			set blnNewWindow to not (strValStart is "n")
  		else if strKey is in {"hm", "HiddenMatchesReported"} then
  			set blnWarnHidden to not (strValStart is "n")
  		else if strKey is in {"cl", "Clear"} then -- Clear
  			if strValStart is "f" then -- clear filters
  				set {strSmartGroup, strState, strFlagged, strDurn} to {"", "", "", ""}
  			else -- clear all
  				set {strSmartGroup, strGrouping, strSorting, strState, strFlagged, strDurn} to {"", "", "", "", "", ""}
  			end if
  		else if strKey is in {"si", "Sidebar"} then
  			set strSmartGroup to strValue
  		else if strKey is in {"gr", "Grouping"} then
  			set strGrouping to strValue
  		else if strKey is in {"so", "Sorting"} then
  			set strSorting to strValue
  		else if strKey is in {"st", "Status"} then
  			if strValue is "remaining" then strValue is "incomplete"
  			set strState to strValue
  		else if strKey is in {"fl", "Flagged"} then
  			set strFlagged to strValue
  		else if strKey is in {"du", "Duration"} then
  			set strDurn to strValue
  		else
  			-- display dialog "Unknown setting: " & strKey
  		end if
  		
  	end repeat
  	set my text item delimiters to space
  	
  	{{blnShortCut, blnNewWindow, blnWarnHidden}, ¬
  		{strSmartGroup, strGrouping, strSorting, strState, strFlagged, strDurn}}
  end ParseComments
  
  on Tokenize(str)
  	set text item delimiters to space
  	set lstParts to text items of str
  	set lstTokens to {}
  	repeat with refPart in lstParts
  		if length of refPart > 0 then
  			set lstTokens to lstTokens & TokenizeBrackets(refPart)
  		end if
  	end repeat
  	
  	set lstUnSigned to {}
  	repeat with oToken in lstTokens
  		set strToken to oToken as string
  		if length of strToken > 1 then
  			set strChar to first character of strToken
  			if strChar is "-" or strChar is "+" then
  				set lstUnSigned to lstUnSigned & {strChar, text 2 thru end of strToken}
  			else
  				set end of lstUnSigned to strToken
  			end if
  		else
  			set end of lstUnSigned to strToken
  		end if
  	end repeat
  	lstUnSigned
  end Tokenize
  
  on TokenizeBrackets(strPhrase)
  	set lstTokens to {}
  	if length of strPhrase = 1 then ¬
  		if strPhrase is in {"(", ")"} then return {strPhrase}
  	set text item delimiters to "("
  	set lstParts to text items of strPhrase
  	set text item delimiters to ")"
  	repeat with oPart in lstParts
  		if length of oPart < 1 then
  			set end of lstTokens to "("
  		else
  			set lstBracketFree to text items of oPart
  			repeat with oFree in lstBracketFree
  				if length of oFree > 0 then
  					set end of lstTokens to contents of oFree
  				else
  					set end of lstTokens to ")"
  				end if
  			end repeat
  		end if
  	end repeat
  	set text item delimiters to space
  	lstTokens
  end TokenizeBrackets
  
  on ReString(lstTokens, strDelimiter)
  	set text item delimiters to strDelimiter
  	set str to lstTokens as text
  	set str to FindReplace(str, "( ", "(")
  	set str to FindReplace(str, " )", ")")
  	set text item delimiters to space
  	str
  end ReString
  
  on DeBracket(lstTokens)
  	set lstNoBrackets to {}
  	repeat with oToken in lstTokens
  		set strToken to contents of oToken
  		if strToken is not "(" then
  			if strToken is not ")" then set end of lstNoBrackets to strToken
  		end if
  	end repeat
  	lstNoBrackets
  end DeBracket
  
  on CheckFirstTokens(lstTokens)
  	-- Check archive status
  	set lstAllFalse to {false, false, false, false}
  	set {blnArchive, blnObject, blnWhere, blnTestRoom} to lstAllFalse
  	set lngTokens to length of lstTokens
  	if lngTokens < 1 then
  		return {lstAllFalse, lstTokens}
  	else
  		set lngArchive to offset of "arc" in (first item of lstTokens)
  		set blnArchive to (lngArchive is 1)
  		if lngArchive > 1 then set lngArchive to 1
  	end if
  	
  	-- Check for object specifier
  	if lngTokens < (lngArchive + 1) then return {{blnArchive, false, false, false}, lstTokens}
  	set strClass to (item (lngArchive + 1) of lstTokens)
  	set blnObject to (strClass is in {"projects", "tasks", "contexts", "folders"})
  	
  	-- Normalize any single form of object specifier to plural 
  	if (not blnObject) then
  		set blnObject to (strClass is in {"project", "task", "context", "folder"})
  		if blnObject then set item (lngArchive + 1) of lstTokens to strClass & "s"
  	end if
  	
  	-- Check for where/whose (unless the query is simply a class name without clauses)
  	if lngTokens < (lngArchive + 2) then
  		if blnObject then
  			return {{blnArchive, true, true, true}, lstTokens}
  		else
  			return {{blnArchive, false, false, false}, lstTokens}
  		end if
  	end if
  	set blnWhere to ((item (lngArchive + 2) of lstTokens) is in {"where", "whose"})
  	
  	-- Check that there is room for a boolean test of the objects
  	set lngTokens to length of DeBracket(lstTokens)
  	set blnTestRoom to (lngTokens ≥ (lngArchive + 3)) -- was 5 but reduced to allow for [ABBREVNS]
  	
  	return {{blnArchive, blnObject, blnWhere, blnTestRoom}, lstTokens}
  end CheckFirstTokens
  
  on ClassInteger(strClass)
  	if strClass is "folders" then
  		1
  	else if strClass is "projects" then
  		2
  	else if strClass is "tasks" then
  		3
  	else if strClass is "contexts" then
  		4
  	else
  		0
  	end if
  end ClassInteger
  
  on IntegerClass(lng)
  	if lng is 1 then
  		"folders"
  	else if lng is 2 then
  		"projects"
  	else if lng is 3 then
  		"tasks"
  	else if lng is 4 then
  		"contexts"
  	else
  		""
  	end if
  end IntegerClass
  
  on pl(lngNum, strTerm)
  	if absolute(lngNum) is not 1 then
  		strTerm & "s"
  	else
  		strTerm
  	end if
  end pl
  
  on absolute(num)
  	if num < 0 then
  		-num
  	else
  		num
  	end if
  end absolute
  
  on IconFile()
  	set strPath to path to resource "applet.icns"
  	strPath as alias
  end IconFile
  
  on RunningOmniFocus()
  	if not IsRunning("OmniFocus") then
  		tell application id "com.apple.systemevents"
  			activate
  			display dialog "OmniFocus is not running ..." buttons {"Exit"} default button {"Exit"} with title pTitle with icon my IconFile()
  		end tell
  		return false
  	else
  		return true
  	end if
  end RunningOmniFocus
  
  on ScriptFolder(blnPackage)
  	set oPath to (path to me) as alias
  	if blnPackage then
  		return oPath
  	else
  		tell application id "MACS" to return container of oPath
  	end if
  end ScriptFolder
  
  on ReportZeroMatches(strQuery, lngClass)
  	set strClass to IntegerClass(lngClass)
  	try
  		tell application id "com.apple.systemevents"
  			activate
  			display dialog "(0) " & strClass & "  match the search:" & ¬
  				return & return & tab & strQuery buttons {"OK"} default button {"OK"} with title pTitle with icon my IconFile()
  		end tell
  	end try
  end ReportZeroMatches
  
  on readFile(unixPath)
  	do shell script "cat " & unixPath
  end readFile
  
  on IsRunning(strName)
  	tell application id "com.apple.systemevents"
  		set lstProcs to (processes where name is strName)
  		return ((length of lstProcs) > 0)
  	end tell
  end IsRunning
  
  on GetDoc(blnArchive)
  	tell application id "OFOC"
  		set docDefault to default document
  		set docArchive to missing value
  		if blnArchive then
  			-- first see if it is already open
  			set lstArchive to documents where name is "Archive"
  			if length of lstArchive > 0 then return first item of lstArchive
  			
  			-- if not open then try to load it from file
  			set strFile to file of docDefault as string
  			set AppleScript's text item delimiters to ":"
  			
  			set lstParts to text items of strFile
  			set lngParts to length of lstParts
  			set item (lngParts - 1) of lstParts to "Archive.ofocus-archive"
  			set alsArchive to (lstParts as rich text) as alias
  			set AppleScript's text item delimiters to space
  			
  			set docArchive to missing value
  			repeat with iWait from 1 to 10
  				try
  					set docArchive to open alsArchive
  					exit repeat
  				on error
  					delay 0.2
  				end try
  			end repeat
  			return docArchive
  		else
  			return docDefault
  		end if
  	end tell
  end GetDoc
  
  on SaveToMRU(strQuery, strTemplate)
  	{strQuery, strTemplate}
  	SaveQuery(strQuery)
  	if length of strTemplate > 0 then ¬
  		if strTemplate is not equal to strQuery then SaveQuery(strTemplate)
  end SaveToMRU
  
  on ReadIntervals2(lstTokens)
  	
  	set lngTokens to count of lstTokens
  	set lngSkip to 0
  	set lstTrans to {}
  	set blnFound to false
  	repeat with i from 1 to lngTokens
  		if lngSkip > 0 then
  			set lngSkip to lngSkip - 1
  		else
  			set strToken to item i of lstTokens
  			if (lngTokens - i) > 1 then
  				if strToken begins with "date \"" then
  					set strOp to item (i + 1) of lstTokens
  					
  					if strOp is in {"+", "-"} then
  						set strInterval to item (i + 2) of lstTokens
  						
  						set strUnit to last character of strInterval
  						if strUnit is in {"d", "m", "w", "y"} then
  							
  							set dte to run script strToken
  							set dte to DatePlus(dte, strOp & strInterval)
  							if dte is not missing value then
  								if length of strToken > 18 then
  									set end of lstTrans to "date \"" & (dte as string) & quote
  								else
  									set end of lstTrans to "date \"" & (short date string of dte) & quote
  								end if
  								set blnFound to true
  								set lngSkip to 2 -- (we've already made use of the next two tokens)
  							else -- not an interval -- pass through
  								set end of lstTrans to strToken
  							end if
  						else -- not a unit - pass through
  							set end of lstTrans to strToken
  						end if
  					else -- not an operator - pass through
  						set lstTrans to lstTrans & {strToken, strOp}
  						set lngSkip to 1
  					end if
  				else -- not a date - pass through
  					set end of lstTrans to strToken
  				end if
  			else -- not enough tokens left for triad - pass through
  				set end of lstTrans to strToken
  			end if
  		end if
  	end repeat
  	{blnFound, lstTrans}
  end ReadIntervals2
  
  
  on DatePlus(dte, strNUnits)
  	copy dte to dteNew
  	if length of strNUnits < 2 then return missing value
  	set strUnit to last character of strNUnits
  	try
  		set lngDelta to (text 1 thru -2 of strNUnits) as integer
  	on error
  		return missing value
  	end try
  	ignoring case
  		if strUnit = "d" then
  			return dteNew + lngDelta * days
  		else if strUnit = "w" then
  			return dteNew + lngDelta * weeks
  		else if strUnit = "y" then
  			set (year of dteNew) to (year of dteNew) + lngDelta
  			return dteNew
  		else if strUnit = "m" then
  			-- Get current month and year
  			set lngMonth to (month of dteNew) * 1
  			set lngYear to (year of dteNew)
  			
  			-- and simply add the increment to the month, 
  			-- negative possibly getting something negative, and/or too large
  			set lngNewMonth to lngMonth + lngDelta
  			
  			-- get the YEAR
  			set lngDateMonth to lngNewMonth mod 12
  			set lngYearDelta to lngNewMonth div 12
  			
  			-- if we have gone down to a negative month, we are already in the previous year, 
  			-- regardless of any multiples of 12
  			if lngDateMonth ≤ 0 then set lngYearDelta to (lngYearDelta - 1)
  			if lngYearDelta is not 0 then
  				set lngDateYear to lngYear + lngYearDelta
  			else
  				set lngDateYear to lngYear
  			end if
  			
  			-- and the MONTH
  			if lngDateMonth is 0 then
  				set lngDateMonth to 12
  			else if lngDateMonth < 0 then
  				set lngDateMonth to (12 + lngDateMonth)
  			end if
  			
  			-- and update the date variable
  			if lngYear is not lngDateYear then set (year of dteNew) to lngDateYear
  			if lngMonth is not lngDateMonth then set (month of dteNew) to lngDateMonth
  		else
  			return missing value
  		end if
  	end ignoring
  	dteNew
  end DatePlus
  
  on ReadDateTags(lstTokens)
  	set blnFound to false
  	repeat with iToken from 1 to length of lstTokens
  		set strToken to contents of (item iToken of lstTokens)
  		if length of strToken is 5 then
  			if strToken is in plstMonthTags then
  				set strMonth to Tag2ASDate(text 2 thru 4 of strToken)
  				set item iToken of lstTokens to strMonth
  				set blnFound to true
  			end if
  		end if
  	end repeat
  	{blnFound, lstTokens}
  end ReadDateTags
  
  on Tag2ASDate(strMonthTag)
  	if length of strMonthTag ≠ 3 then return missing value
  	set lngMonth to ((offset of strMonthTag in "janfebmaraprmayjunjulaugsepoctnovdec") + 2) div 3
  	if lngMonth < 1 then return missing value
  	set dteBase to (current date)
  	set lngThisMonth to month of dteBase
  	set month of dteBase to lngMonth
  	if lngMonth < lngThisMonth then set year of dteBase to (year of dteBase) + 1
  	set day of dteBase to 1
  	"date " & quote & short date string of dteBase & quote
  end Tag2ASDate
  
  on ReadRelvDates(lstTokens)
  	set blnFound to false
  	
  	repeat with iToken from 1 to length of lstTokens
  		set strToken to item iToken of lstTokens
  		if strToken is "now" then
  			set item iToken of lstTokens to "date \"" & ((current date) as string) & quote
  			set blnFound to true
  			-- exit repeat
  		else if strToken is "soon" then
  			tell application id "OFOC"
  				set item iToken of lstTokens to "date \"" & (((current date) + ((value of setting id "DueSoonInterval" of default document) as integer)) as string) & quote
  			end tell
  			set blnFound to true
  			-- exit repeat
  		else if strToken is "today" then
  			set item iToken of lstTokens to "date \"" & short date string of (current date) & quote
  			set blnFound to true
  			-- exit repeat
  		else if strToken is "tomorrow" then
  			set item iToken of lstTokens to "date \"" & short date string of ((date (short date string of (current date))) + days) & quote
  			set blnFound to true
  			-- exit repeat
  		else if strToken is "yesterday" then
  			set item iToken of lstTokens to "date \"" & short date string of ((date (short date string of (current date))) - days) & quote
  			set blnFound to true
  			-- exit repeat
  		end if
  	end repeat
  	
  	return {blnFound, lstTokens}
  end ReadRelvDates
  
  -- PLACE A SUCCESSFUL QUERY (OR QUERY TEMPLATE) AT THE TOP OF THE MRU LIST
  on SaveQuery(strLegibleQuery)
  	try
  		glstMRU
  	on error
  		set glstMRU to {}
  	end try
  	set text item delimiters to return
  	set text item delimiters to space
  	if glstMRU does not contain strLegibleQuery then
  		set lngMax to plngMRUHistory - 1
  		if length of (glstMRU) > lngMax then ¬
  			set (glstMRU) to ¬
  				items 1 thru lngMax of (glstMRU)
  		set beginning of (glstMRU) to strLegibleQuery
  	else -- already in there, so move to front
  		set iPosn to indexOfItemInList(glstMRU, strLegibleQuery)
  		set glstMRU to DeleteNthFromList(glstMRU, iPosn)
  		set text item delimiters to return
  		set text item delimiters to space
  		set beginning of glstMRU to strLegibleQuery
  	end if
  end SaveQuery
  
  on indexOfItemInList(lst, oItem)
  	set i to 0
  	repeat with i from 1 to (length of lst)
  		if (item i of lst) is equal to oItem then return i
  	end repeat
  	i
  end indexOfItemInList
  
  on DeleteNthFromList(lst, lngN)
  	set lstNew to {}
  	repeat with iPos from 1 to length of lst
  		if iPos is not lngN then ¬
  			set end of lstNew to item iPos of lst
  	end repeat
  	lstNew
  end DeleteNthFromList
  
  to FindReplace(strText, strFind, strReplace)
  	if the strText contains strFind then
  		set AppleScript's text item delimiters to strFind
  		set lstParts to text items of strText
  		set AppleScript's text item delimiters to strReplace
  		set strText to lstParts as string
  		set AppleScript's text item delimiters to space
  	end if
  	return strText
  end FindReplace
  
  on GetAbbrevns(strClass, blnDefinitions)
  	-- Start by getting any definition of [current] for this class
  	pAbbrevnFile
  	set strAbbrevnPath to GetLibPath(pAbbrevnFile)
  	set strRows to readFile(strAbbrevnPath)
  	set lstLines to paragraphs of strRows
  	
  	set lstAbbrevns to {}
  	-- add any definition of [current] at the start of the abbrev list
  	
  	-- Allow for multiple synonyms [Term1]|[Term]|[TermN]->Expansion text
  	-- delimited by pipe bar
  	set blnNotTask to strClass is not equal to "tasks"
  	if blnNotTask then set strClass to strClass & "]"
  	set text item delimiters to "->"
  	repeat with oLine in lstLines
  		set lstParts to text items of oLine
  		if (length of lstParts > 1) and (oLine contains "[") and (oLine contains "]") then
  			if blnDefinitions then
  				set {strTerms, strDefinition} to items 1 thru 2 of lstParts
  			else
  				set strTerms to first item of lstParts
  			end if
  			
  			set text item delimiters to "|"
  			set lstTerms to text items of strTerms
  			
  			set text item delimiters to "-"
  			set blnSynonym to false
  			repeat with oTerm in lstTerms
  				-- Ignore it if it is a definition of current
  				set lstSegments to text items of oTerm
  				set lngSegments to length of lstSegments
  				if lngSegments > 1 then
  					-- if the abbreviation does not terminate in hyphen+classname then discard it
  					-- otherwise pass it through, stripped of its class tag
  					strClass
  					last item of lstSegments
  					if (last item of lstSegments) = strClass then
  						set strTerm to my ReString(items 1 thru (lngSegments - 1) of lstSegments, "-") & "]"
  						if blnDefinitions then
  							set end of lstAbbrevns to {strTerm, strDefinition}
  						else
  							if not blnSynonym then set end of lstAbbrevns to strTerm as string
  						end if
  					end if
  				else
  					if not blnNotTask then
  						set strTerm to oTerm as string
  						if blnDefinitions then
  							set end of lstAbbrevns to {strTerm, strDefinition}
  						else
  							if not blnSynonym then set end of lstAbbrevns to strTerm as string
  						end if
  					end if
  				end if
  				set blnSynonym to true
  			end repeat
  			set text item delimiters to "->"
  		end if
  	end repeat
  	set text item delimiters to space
  	lstAbbrevns
  end GetAbbrevns
  
  
  on GetLibPath(strLibFile)
  	set strFolder to ScriptFolder(my pblnLibsInPackage) as string
  	set strLibPath to POSIX path of (strFolder & strLibFile)
  	tell application "Finder"
  		if not (exists (strLibPath as POSIX file)) then
  			set strPackFolder to my ScriptFolder(true) as string
  			set strPackageLibPath to (strPackFolder & strLibFile)
  			duplicate strPackageLibPath to strFolder
  		end if
  	end tell
  	strLibPath
  end GetLibPath
  
  --------------------------------------------------------------------------------------------------------
  --- CLASSES: sMenu, sSearch sDisplay
  
  script sMenu
  	property spMenuQuery : ""
  	property spMenuComments : ""
  	
  	property spStartMenu : ""
  	property spEntryPrompt : ""
  	property spEntryDraft : ""
  	
  	property pcmdTag : "- "
  	property pcmdLib : pcmdTag & "LIBRARY of searches"
  	property pcmdMRU : pcmdTag & "Search History"
  	property pcmdManual : pcmdTag & "ENTER NEW SEARCH" -- replace pnewquery
  	property pcmdOptions : pcmdTag & "Global search defaults:  "
  	property pcmdEditLib : pcmdTag & "Edit Library"
  	property pcmdAbbrevns : pcmdTag & "Edit Abbreviations"
  	property pcmdClearMRU : pcmdTag & "CLEAR search history"
  	
  	property pBar : {"__________________________"}
  	property plstCoreMenu : {pcmdManual}
  	property plstLibMenu : {pcmdMRU, pcmdEditLib, pcmdAbbrevns}
  	property plstMRUMenu : {pcmdLib, pcmdClearMRU}
  	property pBarMRU : {"SEARCH HISTORY:"}
  	property pBarLIB : {"LIBRARY:"}
  	
  	property plstDropDowns : {"Sidebar", "Grouping", "Sorting", "Status", "Flagged", "Duration"}
  	
  	script sEntry -- of sMenu
  		on EntryMenu(strDraft, strPrompt)
  			set strProcess to " Run search "
  			set varChoice to "Continue"
  			set varLastChoice to varChoice
  			
  			set lstMRU to sMenu's GetMRU()
  			if length of lstMRU > 0 then tell application id "com.apple.finder" to set the clipboard to first item of lstMRU
  			
  			if length of strPrompt < 1 then set strPrompt to "(projects|tasks|contexts|folders)  where  (boolean test)" & return & return & ¬
  				"boolean test can include (<txt>|<num>|<dte>) placeholders, " & return & ¬
  				"(the user will be prompted to enter particular values)" & return & return & ¬
  				"(for different placeholders of the same type: <txt2> ... <txt9> etc)" & return & return & ¬
  				"e.g." & return & tab & "projects where (completed is false) and ((<txt> is in name)" & return & tab & " and (<txt2> is in name))"
  			set {strSkip, strAssisted, strSpacer} to {"Cancel", "Assisted search editing", "---------------------------------------------"}
  			
  			set blnContinue to true
  			set lstProps to {}
  			repeat while blnContinue
  				-- put up the main menu displaying strDraft
  				set strDraft to sMenu's Trim(strDraft) & return & return
  				tell application id "com.apple.systemevents"
  					activate
  					set recResponse to display dialog strPrompt default answer strDraft buttons ¬
  						{strSkip, strAssisted, strSpacer & strProcess & strSpacer} default button 3 with title my pTitle with icon my IconFile()
  				end tell
  				
  				set strButton to button returned of recResponse
  				if strButton is strSkip then
  					return false
  				else if strButton is strAssisted then
  					set strQuery to sMenu's Trim(text returned of recResponse)
  					set {strQuery, lstComments} to sMenu's SplitComments(strQuery)
  					set lstTokens to my Tokenize(strQuery)
  					set lstNoBrackets to my DeBracket(lstTokens)
  					set {{blnArchive, blnObject, blnWhere, blnTestRoom}, lstNoBrackets} to my CheckFirstTokens(lstNoBrackets)
  					
  					set {strProjects, strTasks, strContexts, strFolders} to {"projects", "tasks", "contexts", "folders"}
  					
  					-- ENSURE THAT A CLASS HAS BEEN CHOSEN
  					-- AND THAT A VARIABLE POINTS TO A MATCHING PROPERTY LIST
  					if blnObject then
  						set lngArchive to 0
  						if blnArchive then set lngArchive to 1
  						set strClass to item (lngArchive + 1) of lstTokens
  					else
  						tell application id "com.apple.systemevents"
  							activate
  							set varObject to choose from list {strProjects, strTasks, strContexts, strFolders} default items {strProjects} with title my pTitle ¬
  								with prompt "Choose type of object to search for:"
  						end tell
  						if varObject is not false then
  							set strClass to first item of varObject
  						else
  							set strClass to ""
  						end if
  					end if
  					
  					if length of strClass > 0 then
  						set strPrompt to "Search for " & strClass & ":"
  						set lstAbbrevns to my GetAbbrevns(strClass, false)
  						set lstProps to {}
  						if length of lstAbbrevns > 0 then set lstProps to {"AVAILABLE ABBREVIATIONS:"} & lstAbbrevns
  						if strClass is strProjects then
  							set lstProps to lstProps & PropNames(ProjProps())
  						else if strClass is strTasks then
  							set lstProps to lstProps & PropNames(TaskProps())
  						else if strClass is strContexts then
  							set lstProps to lstProps & PropNames(ContextProps())
  						else if strClass is strFolders then
  							set lstProps to lstProps & PropNames(FolderProps())
  						else
  							set lstProps to {}
  						end if
  						
  						-- ENSURE THAT WE HAVE  <PLURALCLASS> + WHERE
  						set lngArchive to 0
  						if blnArchive then set lngArchive to 1
  						set lngTokens to length of lstTokens
  						repeat while lngTokens < (lngArchive + 2)
  							set end of lstTokens to ""
  							set lngTokens to length of lstTokens
  						end repeat
  						set item (lngArchive + 1) of lstTokens to strClass
  						set item (lngArchive + 2) of lstTokens to "where"
  						
  						set text item delimiters to space
  						set strDraft to my ReString(lstTokens, space)
  						
  						-- GET A SINGULAR VERSION OF THE CLASS NAME
  						set strSingleClass to (text 1 thru -2 of strClass) as text
  						
  						if length of lstProps > 0 then
  							tell application id "com.apple.systemevents"
  								activate
  								set varSelns to choose from list lstProps with title my pTitle with prompt ¬
  									"Search for " & strClass & " - choose properties to search by ..." & return & return & ¬
  									"select multiple properties with Cmd-Click" OK button name "Use selected " & strSingleClass & " properties" cancel button name ¬
  									"Skip this list" with multiple selections allowed and empty selection allowed
  							end tell
  							if varSelns is not false then
  								set {_, lstTagDeltas} to TagIncrements(strDraft, {0, 0, 0})
  								
  								-- GET ADDITIONAL ASSISTED CLAUSE(S) / COMMENT(S)
  								set strAsstDraft to QueryDraft(strClass, varSelns, lstTagDeltas)
  								
  								set text item delimiters to "--"
  								set lstParts to text items of strAsstDraft
  								set text item delimiters to space
  								
  								if length of lstParts > 0 then
  									-- Any clauses will be in the first item, which will be "" in the absence of clauses
  									set strClauses to first item of lstParts
  									if strClauses is not "" then
  										if lngTokens > (lngArchive + 2) then -- if there is already material after '|classname| where'
  											if last item of lstTokens is not in {"and", "or", "not"} then
  												set strDraft to strDraft & " and " & strClauses
  											else
  												set strDraft to strDraft & " (" & strClauses & ")"
  											end if
  										else
  											set strDraft to strDraft & space & strClauses
  										end if
  									end if
  									
  									-- All subsequent parts will contain comments
  									if length of lstParts > 1 then
  										repeat with iComment from 2 to length of lstParts
  											set end of lstComments to (item iComment of lstParts) as string
  										end repeat
  										
  									end if
  									if length of lstComments > 0 then ¬
  										set strDraft to strDraft & " --" & my ReString(lstComments, space)
  								end if
  							end if
  						end if
  					end if
  				else if strButton contains strProcess then
  					set strDraft to sMenu's Trim(text returned of recResponse)
  					set blnContinue to false
  				end if
  			end repeat
  			return strDraft
  		end EntryMenu
  		
  		on QueryDraft(strObjectClass, varSelns, lstTagDeltas)
  			set strDraft to ""
  			set strComment to ""
  			set blnFirst to true
  			if varSelns is not false then
  				set lstNewTagDeltas to lstTagDeltas
  				set lngFields to length of varSelns
  				repeat with oField in varSelns
  					set strField to oField as string
  					if first character of oField is "[" and last character of oField is "]" then
  						set strClause to strField
  						if length of strClause > 0 then
  							if blnFirst then
  								set strDraft to strDraft & strClause
  								if lngFields > 1 then set strDraft to "(" & strDraft & ")"
  								set blnFirst to false
  							else
  								set strDraft to strDraft & " and (" & strClause & ")"
  							end if
  						end if
  					else
  						set text item delimiters to " - "
  						set lstParts to text items of strField
  						if length of lstParts > 1 then
  							set strField to first item of lstParts
  							
  							set strPropClass to item 2 of lstParts
  							if (offset of "/" in strPropClass) > 0 then
  								set text item delimiters to " / "
  								set lstClasses to text items of strPropClass
  							else
  								set lstClasses to {strPropClass}
  							end if
  							
  							set strPrompt to strObjectClass & " where"
  							set strClause to FieldClause(strPrompt, strField, lstClasses)
  							set {strClause, lstNewTagDeltas} to TagIncrements(strClause, lstNewTagDeltas)
  							
  							if length of strClause > 0 then
  								if blnFirst then
  									set strDraft to strDraft & strClause
  									if lngFields > 1 then set strDraft to "(" & strDraft & ")"
  									set blnFirst to false
  								else
  									set strDraft to strDraft & " and (" & strClause & ")"
  								end if
  							end if
  						else
  							if (text 1 thru 2 of strField) is equal to "--" then
  								set strComment to strComment & GetComment(strObjectClass is "contexts")
  								if length of strComment > 1 then
  									if text 1 thru 2 of strComment is not "--" then set strDraft to strDraft & "--" & strComment
  								end if
  							end if
  						end if
  					end if
  				end repeat
  			end if
  			set text item delimiters to space
  			strDraft
  		end QueryDraft
  		
  		on FieldClause(strPrompt, strField, lstClasses)
  			-- for each class, add one or more options to the option list
  			
  			set lstOptions to ClauseOptions(strField, lstClasses)
  			tell application id "com.apple.systemevents"
  				activate
  				set varChoice to choose from list lstOptions with prompt strPrompt with title my pTitle
  			end tell
  			if varChoice is false then
  				""
  			else
  				first item of varChoice
  			end if
  		end FieldClause
  		
  		on ClauseOptions(strField, lstClasses)
  			set lstOptions to {}
  			set {strTrue, strFalse, strMissing} to {"true", "false", "missing value"}
  			set {strIS, strNOT, strGE, strLE, strMORE, strLESS} to {" is ", " is not ", " ≥ ", " ≤ ", " > ", " < "}
  			set strAND to " and "
  			repeat with iClass from length of lstClasses to 1 by -1
  				set strClass to (item iClass of lstClasses) as string
  				if strClass is strMissing then
  					set end of lstOptions to "its " & strField & strIS & strMissing
  					set end of lstOptions to "its " & strField & strNOT & strMissing
  				else if strClass is "project status" then
  					set {strActive, strDone, strDropped, strOnHold} to {"active", "done", "dropped", "on hold"}
  					set end of lstOptions to strField & strIS & strActive
  					set end of lstOptions to strField & strIS & strDone
  					set end of lstOptions to strField & strIS & strDropped
  					set end of lstOptions to strField & strIS & strOnHold
  					set end of lstOptions to strField & strNOT & strActive
  					set end of lstOptions to strField & strNOT & strDone
  					set end of lstOptions to strField & strNOT & strDropped
  					set end of lstOptions to strField & strNOT & strOnHold
  				else if strClass is "boolean" then
  					set end of lstOptions to strField & strIS & strTrue
  					set end of lstOptions to strField & strIS & strFalse
  				else if strClass is "date" then
  					set end of lstOptions to strField & strLESS & "today"
  					set end of lstOptions to strField & strLE & "today"
  					set end of lstOptions to strField & strIS & "today"
  					set end of lstOptions to strField & strGE & "today"
  					set end of lstOptions to strField & strMORE & "today"
  					set end of lstOptions to strField & strLESS & "soon"
  					set end of lstOptions to strField & strLE & "soon"
  					set end of lstOptions to strField & strIS & "soon"
  					set end of lstOptions to strField & strGE & "soon"
  					set end of lstOptions to strField & strMORE & "soon"
  					set end of lstOptions to strField & strLESS & "<dte>"
  					set end of lstOptions to strField & strLE & "<dte>"
  					set end of lstOptions to strField & strIS & "<dte>"
  					set end of lstOptions to strField & strGE & "<dte>"
  					set end of lstOptions to strField & strMORE & "<dte>"
  					set end of lstOptions to strField & strIS & "<dte>"
  					set end of lstOptions to "(" & strField & strGE & "<dte1>)" & ¬
  						strAND & "(" & strField & strLE & "<dte2>" & ")"
  				else if strClass is "integer" then
  					set end of lstOptions to strField & strLESS & "<num>"
  					set end of lstOptions to strField & strIS & "<num>"
  					set end of lstOptions to strField & strMORE & "<num>"
  					set end of lstOptions to "(" & strField & strGE & "<num1>)" & ¬
  						strAND & "(" & strField & strLE & "<num2>)"
  				else if strClass is "unicode text" then
  					set end of lstOptions to strField & " contains <txt>"
  					set end of lstOptions to "(<txt1> is in " & strField & ") OR (<txt2> is in " & strField & ")"
  					set end of lstOptions to "(<txt1> is in " & strField & ") AND (<txt2> is in " & strField & ")"
  					set end of lstOptions to strField & " does not contain <txt>"
  					set end of lstOptions to strField & " begins with <txt>"
  					set end of lstOptions to strField & " ends with <txt>"
  					set end of lstOptions to strField & " is <txt>"
  				else if strClass is "repetition interval" then
  					set end of lstOptions to "(steps of " & strField & strIS & "<num>" & ")"
  					set end of lstOptions to "(steps of " & strField & strMORE & "<num>" & ")"
  					set end of lstOptions to "(steps of " & strField & strLESS & "<num>" & ")"
  					set end of lstOptions to "(unit of " & strField & strIS & "day" & ")"
  					set end of lstOptions to "(unit of " & strField & strIS & "week" & ")"
  					set end of lstOptions to "(unit of " & strField & strIS & "month" & ")"
  					set end of lstOptions to "(unit of " & strField & strIS & "year" & ")"
  					set end of lstOptions to "(fixed of " & strField & strIS & strTrue & ")"
  					set end of lstOptions to "(fixed of " & strField & strIS & strFalse & ")"
  					set end of lstOptions to strField & " = {unit:day, fixed:false, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:week, fixed:false, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:month, fixed:false, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:year, fixed:false, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:day, fixed:true, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:week, fixed:true, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:month, fixed:true, steps:<num>}"
  					set end of lstOptions to strField & " = {unit:year, fixed:true, steps:<num>}"
  				else if strClass is "geocode" then
  					set end of lstOptions to "(name of " & strField & ")" & " contains <txt>"
  					set end of lstOptions to "(latitude of " & strField & ")" & " > <num>"
  					set end of lstOptions to "(latitude of " & strField & ")" & " < <num>"
  					set end of lstOptions to "(longitude of " & strField & ")" & " > <num>"
  					set end of lstOptions to "(longitude of " & strField & ")" & " < <num>"
  					set end of lstOptions to "(radius of " & strField & ")" & " > <num>"
  					set end of lstOptions to "(radius of " & strField & ")" & " < <num>"
  				else if strClass is "folder" then
  					set lstOptions to lstOptions & FolderClauses()
  				else if strClass is "context" then
  					set lstOptions to lstOptions & ContextClauses()
  				else -- document 
  					set end of lstOptions to "(name of its " & strField & ") contains <txt>"
  				end if
  			end repeat
  			lstOptions
  		end ClauseOptions
  		
  		-- ??
  		on FolderClauses()
  			{"class of its container is folder", "class of its container is document"}
  		end FolderClauses
  		
  		-- ??
  		on ContextClauses()
  			{"name of its context is <txt>", "its context is missing value", "its context is not missing value"}
  		end ContextClauses
  		
  		on GetComment(blnContext)
  			set strComment to ""
  			
  			set strClearAll to "Clear=all		cl			(clear sorting, grouping, and filters)"
  			set strClearFilters to "Clear=filters		cl=f			(clear filters only)"
  			
  			set lstDropDown to SwitchOptions() & pBar & {strClearAll, strClearFilters} & pBar & ¬
  				DropOptions(plstDropDowns, 3)
  			tell application id "com.apple.systemevents"
  				activate
  				set varSelns to choose from list lstDropDown with title my pTitle with prompt ¬
  					"Group, sort or filter the view ..." & return & return & ¬
  					"Select multiple settings with Cmd-Click" OK button name "Specify selected settings" cancel button name ¬
  					"Skip this list" with multiple selections allowed and empty selection allowed
  			end tell
  			tell application "OmniFocus"
  				tell default document
  					set oWin to front document window
  				end tell
  				if varSelns is not false then
  					if blnContext then
  						if (selected view mode identifier) of oWin is not "context" then set (selected view mode identifier) of oWin to "context"
  					else
  						if (selected view mode identifier) of oWin is not "project" then set (selected view mode identifier) of oWin to "project"
  					end if
  					
  					set blnFirst to true
  					tell content of oWin
  						set lstNoChange to {"[keep existing]"}
  						set lstOptions to {}
  						set my text item delimiters to tab
  						--varSelns
  						repeat with iSetting from 1 to length of varSelns
  							set item iSetting of varSelns to (first text item of item iSetting of varSelns)
  						end repeat
  						varSelns
  						set my text item delimiters to space
  						repeat with oSetting in varSelns
  							set oSetting to contents of oSetting
  							if oSetting is "Sidebar" then
  								set lstOptions to lstNoChange & (available smart group identifiers of sidebar of oWin)
  							else if oSetting is "Grouping" then
  								set lstOptions to lstNoChange & (available grouping identifiers)
  							else if oSetting is "Sorting" then
  								set lstOptions to lstNoChange & (available sorting identifiers)
  							else if oSetting is "Status" then
  								set lstOptions to lstNoChange & (available task state filter identifiers)
  							else if oSetting is "Flagged" then
  								set lstOptions to lstNoChange & (available task flagged filter identifiers)
  							else if oSetting is "Duration" then
  								set lstOptions to lstNoChange & (available task duration filter identifiers)
  							else if (oSetting is strClearAll) or (oSetting is strClearFilters) then
  								set lstOptions to {}
  							else
  								--
  							end if
  							set strComment to my AddSetting(strComment, oSetting, lstOptions)
  						end repeat
  						set my text item delimiters to space
  					end tell
  				end if
  			end tell
  			return strComment
  		end GetComment
  		
  		on AddSetting(strComment, oSetting, lstOptions)
  			if oSetting is (pBar) as string then return strComment
  			set blnDropDown to oSetting is in plstDropDowns
  			if blnDropDown then
  				set lstSettings to {}
  				set blnSidebar to oSetting is "Sidebar"
  				set my text item delimiters to "-"
  				repeat with oOption in lstOptions
  					set strOption to (contents of oOption) as string
  					if blnSidebar then
  						set lstParts to text items of strOption
  						set lngParts to length of lstParts
  						if lngParts > 1 then
  							set end of lstSettings to (items 1 thru (lngParts - 1) of lstParts) as string
  						else
  							set end of lstSettings to strOption
  						end if
  					else
  						set end of lstSettings to strOption
  					end if
  				end repeat
  				set my text item delimiters to space
  				tell application id "com.apple.systemevents"
  					activate
  					set varSelns to choose from list (lstSettings) default items item 2 of lstSettings with title my pTitle with prompt oSetting & ¬
  						"=" OK button name "Choose selected setting" cancel button name ¬
  						"Skip this list" with empty selection allowed
  				end tell
  				if varSelns is not false then
  					if length of varSelns > 0 then
  						if length of strComment > 0 then
  							set strComment to strComment & " "
  							set blnFirst to false
  						end if
  						set strValue to first item of varSelns
  						if first character of strValue is "[" then set strValue to "-"
  						set strAbbrevn to lowerinitial(text 1 thru 2 of oSetting)
  						set strComment to strComment & strAbbrevn & "=" & strValue
  					end if
  				end if
  			else
  				set strFlag to oSetting as string
  				
  				set my text item delimiters to "="
  				set lstParts to text items of strFlag
  				set strFlag to first item of lstParts
  				if length of lstParts > 1 then
  					set strValue to "=" & first character of (item 2 of lstParts)
  				else
  					set strValue to ""
  				end if
  				
  				if strFlag is "Shortcut" then
  					set strComment to strComment & "sh" & strValue & " "
  				else if strFlag is "NewWindow" then
  					set strComment to strComment & "nw" & strValue & " "
  				else if strFlag is "HiddenMatchesReported" then
  					set strComment to strComment & "hm" & strValue & " "
  				else if strFlag is "clear" then
  					set strComment to strComment & "cl" & strValue & " "
  				else
  					set strComment to strComment & strFlag & " "
  				end if
  				
  				set my text item delimiters to space
  			end if
  			strComment
  		end AddSetting
  		
  		on DropOptions(lstDrop, lngTab)
  			set lstOptions to {}
  			set strTabs to ""
  			repeat with iTab from 1 to lngTab
  				set strTabs to strTabs & tab
  			end repeat
  			if lngTab > 1 then set strShortTab to text 2 thru end of strTabs
  			repeat with oDrop in lstDrop
  				set strDrop to contents of oDrop
  				if strDrop is not "Grouping" then
  					set end of lstOptions to oDrop & strTabs & lowerinitial(text 1 thru 2 of oDrop)
  				else
  					set end of lstOptions to "Grouping" & strShortTab & "gr"
  				end if
  			end repeat
  			lstOptions
  		end DropOptions
  		
  		on SwitchOptions()
  			-- property gblnDefaultWarnHidden : true
  			if gblnDefaultShortCut then
  				set strShortCut to "Shortcut=no					[default is Shortcut (or sh)]"
  			else
  				set strShortCut to "Shortcut						[default is Shortcut=no (or sh=n)]"
  			end if
  			
  			if gblnDefaultNewWindow then
  				set strNewWindow to "NewWindow=no				[default is NewWindow (or nw)]"
  			else
  				set strNewWindow to "NewWindow					[default is NewWindow=no (or nw=n)]"
  			end if
  			
  			if gblnDefaultWarnHidden then
  				set strHidden to "HiddenMatchesReported=no	[default is HiddenMatchesReported (or hm)]"
  			else
  				set strHidden to "HiddenMatchesReported		[default is HiddenMatchesReported=no (or hm=n)]"
  			end if
  			{strShortCut, strNewWindow, strHidden}
  		end SwitchOptions
  		
  		on lowerinitial(strChar)
  			if strChar is "" then return ""
  			set strFirst to first character of strChar
  			considering case
  				set lngChar to offset of strFirst in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  				if lngChar > 0 then
  					return (character lngChar of "abcdefghijklmnopqrstuvwxyz") & text 2 thru end of strChar
  				else
  					return strChar
  				end if
  			end considering
  		end lowerinitial
  		
  		on TagIncrements(strSearch, lstDeltas)
  			-- Disassemble and Rebuild the string with increments for each kind of tag
  			set lstTagStems to {"<txt", "<num", "<dte"}
  			set lstNewDeltas to {}
  			set strClause to strSearch
  			
  			-- Repeatedly disassemble and reassemble (with modifications)
  			
  			repeat with iTagType from 1 to length of lstDeltas
  				-- get the segments delimited by the stem
  				set {strStem, lngDelta} to {item iTagType of lstTagStems, item iTagType of lstDeltas}
  				set lngMax to 0
  				
  				set text item delimiters to strStem
  				set lstParts to text items of strClause
  				set lngParts to count of lstParts
  				
  				-- if any segmentation has occurred (ie if at least one stem is present)
  				if lngParts > 1 then
  					set strClause to first item of lstParts
  					set text item delimiters to ">"
  					
  					-- subdivide any elements following tag(s) into before and after ">"
  					-- ie look for any number in pattern <text n >
  					repeat with iSub from 2 to lngParts
  						set lstSubParts to text items of item iSub of lstParts
  						set strVal to first item of lstSubParts
  						-- if there is a string between the end of the stem and the closing ">"
  						-- try to read it as an integer ...  (reporting any error)
  						set lngVal to 0
  						if length of strVal > 0 then
  							try
  								set lngVal to (strVal as integer) + lngDelta
  							on error
  								tell application id "com.apple.systemevents"
  									activate
  									display dialog "Ill-formed tag:	" & strStem & strVal & ¬
  										return & return & "in the search:" & return & return & strSearch with title my pTitle with icon my IconFile()
  								end tell
  								return strSearch
  							end try
  						else
  							set lngVal to lngDelta
  						end if
  						if lngVal > lngMax then set lngMax to lngVal
  						if lngVal > 0 then -- rebuild the (possibly updated) tag
  							set strClause to strClause & strStem & (lngVal as string) & ">" & items 2 thru end of lstSubParts
  						else
  							set strClause to strClause & strStem & ">" & items 2 thru end of lstSubParts
  						end if
  					end repeat
  					set end of lstNewDeltas to lngMax
  				else
  					set end of lstNewDeltas to 0
  				end if
  			end repeat
  			set text item delimiters to space
  			{strClause, lstNewDeltas}
  		end TagIncrements
  		
  		on PropNames(lstProps)
  			set lstNames to {}
  			repeat with oProp in lstProps
  				set {strName, varType} to oProp
  				if length of varType > 0 then
  					if class of varType is list then
  						set strName to strName & " - " & first item of varType & " / " & item 2 of varType
  					else
  						set strName to strName & " - " & varType
  					end if
  				else
  					set strName to strName & ":"
  				end if
  				set end of lstNames to strName
  			end repeat
  			lstNames & {"-- ADD SEARCH OPTIONS"}
  		end PropNames
  		
  		on ProjProps()
  			set lstProps to BasicProps() & ¬
  				{{"PROJECT-SPECIFIC PROPERTIES", ""}, ¬
  					{"default singleton action holder", "boolean"}, ¬
  					{"folder", {"folder", "missing value"}}, ¬
  					{"last review date", "date"}, ¬
  					{"next review date", "date"}, ¬
  					{"next task", {"task", "missing value"}}, ¬
  					{"review interval", "repetition interval"}, ¬
  					{"root task", "task"}, ¬
  					{"singleton action holder", "boolean"}, ¬
  					{"status", "project status"}} & {{"PROPERTIES SHARED WITH TASKS", ""}} & CoreTaskProps()
  		end ProjProps
  		
  		on FolderProps()
  			set lstProps to BasicProps() & ¬
  				{{"FOLDER-SPECIFIC PROPERTIES", ""}, ¬
  					{"container", {"document", "folder"}}, ¬
  					{"containing document", "document"}, ¬
  					{"creation date", "date"}, ¬
  					{"effectively hidden", "boolean"}, ¬
  					{"hidden", "boolean"}, ¬
  					{"modification date", "date"}}
  		end FolderProps
  		
  		on ContextProps()
  			set lstProps to BasicProps() & {{"CONTEXT-SPECIFIC PROPERTIES", ""}, ¬
  				{"allows next action", "boolean"}, ¬
  				{"available task count", "integer"}, ¬
  				{"container", "context"}, ¬
  				{"effectively hidden", "boolean"}, ¬
  				{"hidden", "boolean"}, ¬
  				{"location", {"geocode", "missing value"}}, ¬
  				{"remaining task count", "integer"}}
  		end ContextProps
  		
  		on TaskProps()
  			set lstProps to BasicProps() & {{"TASK-SPECIFIC PROPERTIES", ""}} & CoreTaskProps()
  		end TaskProps
  		
  		on CoreTaskProps()
  			{{"blocked", "boolean"}, ¬
  				{"completed", "boolean"}, ¬
  				{"completed by children", "boolean"}, ¬
  				{"completion date", "date"}, ¬
  				{"container", "folder"}, ¬
  				{"containing project", {"project", "missing value"}}, ¬
  				{"context", {"context", "missing value"}}, ¬
  				{"creation date", "date"}, ¬
  				{"due date", {"date", "missing value"}}, ¬
  				{"estimated minutes", {"integer", "missing value"}}, ¬
  				{"flagged", "boolean"}, ¬
  				{"in inbox", "boolean"}, ¬
  				{"modification date", "date"}, ¬
  				{"next", "boolean"}, ¬
  				{"number of available tasks", "integer"}, ¬
  				{"number of completed tasks", "integer"}, ¬
  				{"number of tasks", "integer"}, ¬
  				{"parent task", {"task", "missing value"}}, ¬
  				{"repetition", {"repetition interval", "missing value"}}, ¬
  				{"sequential", "boolean"}, ¬
  				{"start date", {"date", "missing value"}}}
  		end CoreTaskProps
  		
  		on BasicProps()
  			{{"BASIC PROPERTIES", ""}, ¬
  				{"name", "unicode text"}, ¬
  				{"note", "unicode text"}, ¬
  				{"id", "unicode text"}}
  		end BasicProps
  	end script -- sEntry
  	
  	on MainLoop()
  		set varChoice to spStartMenu -- copied from file header definition ( may be pcmdMRU or pcmdEditLib )
  		set varLastChoice to varChoice
  		
  		set spMenuQuery to ""
  		set blnQuery to false
  		repeat while not blnQuery -- Outer loop returns us to the menu if labels or dividers are chosen
  			repeat while varChoice begins with pcmdTag
  				set lstCmdTokens to my Tokenize(varChoice)
  				set varLastChoice to varChoice
  				if varChoice is pcmdLib then
  					set varChoice to LibMenu()
  				else if varChoice is pcmdMRU then
  					set varChoice to MRUMenu()
  				else if varChoice is pcmdManual then
  					tell sMenu
  						set varChoice to sEntry's EntryMenu(spEntryDraft, spEntryPrompt)
  					end tell
  					if varChoice is false then set varChoice to pcmdLib
  				else if item 2 of lstCmdTokens is "Global" then
  					SetSearchDefaults()
  					set varChoice to spStartMenu
  				else if varChoice is pcmdEditLib then
  					tell application "TextEdit"
  						set strPath to my GetLibPath(my pLibraryFile)
  						open strPath as POSIX file
  						activate
  					end tell
  					set varChoice to false
  				else if varChoice is pcmdAbbrevns then
  					tell application "TextEdit"
  						set strPath to my GetLibPath(my pAbbrevnFile)
  						open strPath as POSIX file
  						activate
  					end tell
  					set varChoice to false
  				else if varChoice is pcmdClearMRU then
  					set glstMRU to {}
  					set varChoice to MRUMenu()
  				end if
  				-- Has the user hit ESC ?
  				if varChoice is false then
  					return {"", ""}
  				else
  					if class of varChoice is list then set varChoice to first item of varChoice
  				end if
  			end repeat
  			
  			-- we have got past the loop, so the last selection was not a command
  			-- let's assume that it was a query or a query template ...
  			set {spMenuQuery, lstComments} to SplitComments(varChoice)
  			set lstTokens to my Tokenize(spMenuQuery)
  			set {{blnArchive, blnObject, blnWhere, blnTestRoom}, lstTokens} to my CheckFirstTokens(lstTokens)
  			set lngTokens to length of lstTokens
  			set lngArchive to 0
  			if blnArchive then set lngArchive to 1
  			set blnQuery to (blnObject and blnWhere and blnTestRoom) or (blnObject and (lngTokens is lngArchive + 1))
  			set lstErrors to {}
  			if blnQuery then
  				set {lstNumbered, lstErrors} to MatchBrackets(lstTokens)
  				set blnQuery to length of lstErrors is 0
  			end if
  			
  			if blnQuery then -- Normalize "whose" --> "where", "arc" etc, --> "archived" and return
  				if blnArchive then set first item of lstTokens to "archived"
  				if blnWhere then
  					set lngWhere to lngArchive + 2
  					if lngTokens ≥ lngWhere then set item lngWhere of lstTokens to "where"
  				end if
  				set spMenuQuery to my ReString(lstTokens, space)
  				set spMenuComments to ""
  				if length of lstComments > 0 then set spMenuComments to " --" & ReString(lstComments, space)
  				return
  			else -- doesn't work as a query: comment and recycle
  				set spEntryPrompt to CommentFirstTokens(blnObject, blnWhere, blnTestRoom)
  				repeat with oError in lstErrors
  					set spEntryPrompt to spEntryPrompt & return & oError
  				end repeat
  				set spEntryDraft to varChoice
  				set varChoice to varLastChoice
  			end if
  		end repeat
  	end MainLoop
  	
  	on DefaultOptions()
  		-- property gblnDefaultWarnHidden : true
  		if gblnDefaultShortCut then
  			set strShortCut to "Shortcut=yes						(skips choose list, jumps straight to matched objects)"
  		else
  			set strShortCut to "Shortcut=no						(matches are first listed for manual choice)"
  		end if
  		
  		if gblnDefaultNewWindow then
  			set strNewWindow to "NewWindow=yes					(matches are shown in new window)"
  		else
  			set strNewWindow to "NewWindow=no					(matches are shown in existing front window)"
  		end if
  		
  		if gblnDefaultWarnHidden then
  			set strHidden to "HiddenMatchesReported=yes		(warns if filters are hiding matches)"
  		else
  			set strHidden to "HiddenMatchesReported=no		(ignores any matches hidden by filter settings)"
  		end if
  		{strShortCut, strNewWindow, strHidden}
  	end DefaultOptions
  	
  	on LibMenu()
  		set lstLibrary to GetLibrary()
  		set lstShortMRU to GetShortMRU()
  		
  		if length of lstShortMRU > 0 then
  			set strDefault to first item of lstShortMRU
  			set beginning of lstShortMRU to "RECENT SEARCHES:"
  			set end of lstShortMRU to pBar
  		else if length of lstLibrary > 0 then
  			set strDefault to first item of lstLibrary
  		else
  			set strDefault to pcmdManual
  		end if
  		tell application id "com.apple.systemevents"
  			activate
  			set varChoice to choose from list {pcmdOptions & my OptionState()} & plstCoreMenu & plstLibMenu & pBar & lstShortMRU & lstLibrary default items {strDefault} ¬
  				with prompt "LIBRARY OF SEARCHES" with title my pTitle
  		end tell
  		return varChoice
  	end LibMenu
  	
  	on MRUMenu()
  		set lstMRU to GetMRU()
  		if length of lstMRU > 0 then
  			set strDefault to first item of lstMRU
  			set lstStatus to {}
  		else
  			set lstStatus to {"(None)"}
  			set strDefault to pcmdLib
  		end if
  		tell application id "com.apple.systemevents"
  			activate
  			set varChoice to choose from list {pcmdOptions & my OptionState()} & plstCoreMenu & plstMRUMenu & pBar & pBarMRU & lstMRU & lstStatus default items {strDefault} ¬
  				with prompt "SEARCH HISTORY" with title my pTitle
  		end tell
  	end MRUMenu
  	
  	on GetMRU()
  		try
  			glstMRU
  		on error
  			set glstMRU to {}
  		end try
  		return glstMRU
  	end GetMRU
  	
  	
  	
  	
  	on OptionState()
  		set strState to ""
  		set lstValues to {{gblnDefaultShortCut, "ShortCut"}, {gblnDefaultNewWindow, "NewWindow"}, ¬
  			{gblnDefaultWarnHidden, "HiddenMatchesReported"}}
  		repeat with lstPair in lstValues
  			set {blnFlag, strName} to lstPair
  			set strState to strState & "[" & strName
  			if not blnFlag then
  				set strState to strState & "=no]"
  			else
  				set strState to strState & "=yes]"
  			end if
  			set strState to strState & "    "
  		end repeat
  	end OptionState
  	
  	on SetSearchDefaults()
  		set lstDropDown to DefaultOptions()
  		set varSelns to true
  		repeat while not (varSelns is false)
  			tell application id "com.apple.systemevents"
  				activate
  				set varSelns to choose from list lstDropDown with title "Default search options" with prompt ¬
  					"Default search settings:" & return & return & ¬
  					"(Double-click individual settings to toggle them)" OK button name "Toggle value of selected settings" cancel button name ¬
  					"Exit" with multiple selections allowed and empty selection allowed
  			end tell
  			if class of varSelns is list then
  				ignoring case
  					repeat with oSeln in varSelns
  						set strStart to text 1 thru 2 of oSeln
  						if strStart is "sh" then
  							set gblnDefaultShortCut to not gblnDefaultShortCut
  						else if strStart is "ne" then
  							set gblnDefaultNewWindow to not gblnDefaultNewWindow
  						else if strStart is "hi" then
  							set gblnDefaultWarnHidden to not gblnDefaultWarnHidden
  						end if
  					end repeat
  				end ignoring
  				set lstDropDown to DefaultOptions()
  			end if
  		end repeat
  	end SetSearchDefaults
  	
  	on GetLibrary() -- Try to get the library file from the same folder as the script. If not there, copy from package.
  		set strLibPath to my GetLibPath(my pLibraryFile)
  		set strRows to my readFile(strLibPath)
  		tell application id "com.apple.systemevents"
  			activate
  			if strRows is "" then display dialog "Library empty or not found" with title my pTitle
  		end tell
  		paragraphs of strRows
  	end GetLibrary
  	
  	on GetShortMRU()
  		set lstMRU to GetMRU()
  		set lngMRU to length of lstMRU
  		if lngMRU > 0 then
  			if my plngShortMRU < lngMRU then set lngMRU to my plngShortMRU
  			items 1 thru lngMRU of lstMRU
  		else
  			{}
  		end if
  	end GetShortMRU
  	
  	on SplitComments(strQuery)
  		-- separate Query from any Comment
  		set text item delimiters to "--"
  		set lstParts to text items of strQuery
  		set text item delimiters to space
  		set lngParts to length of lstParts
  		set strQuery to ""
  		if lngParts > 0 then set strQuery to first item of lstParts
  		
  		set lstComments to {}
  		if lngParts > 1 then --set lstComments to items 2 through lngParts of lstParts
  			repeat with iPart from 2 to lngParts
  				set strPart to (item iPart of lstParts) as string
  				if strPart is not "" then set end of lstComments to Trim(strPart)
  			end repeat
  		end if
  		{strQuery, lstComments}
  	end SplitComments
  	
  	-- Remove trailling and leading spaces  and internal char 10
  	on Trim(strText)
  		set lngChars to length of strText
  		if lngChars is 0 then return ""
  		set lstWhite to {space, tab, return, ASCII character 10, ASCII character 0}
  		
  		repeat with iChar from 1 to lngChars
  			if character iChar of strText is not in lstWhite then exit repeat
  		end repeat
  		set strText to text iChar thru lngChars of strText
  		
  		repeat with iChar from length of strText to 1 by -1
  			if character iChar of strText is not in lstWhite then exit repeat
  		end repeat
  		set strText to text 1 thru iChar of strText
  		
  		set text item delimiters to ASCII character 10
  		set lstParts to text items of strText
  		set text item delimiters to space
  		lstParts as text
  	end Trim
  	
  	on MatchBrackets(lstTokens)
  		set lstNumbered to {}
  		set lstErrors to {}
  		set lngLevel to 0
  		repeat with oToken in lstTokens
  			set strToken to contents of oToken
  			if strToken is "(" then
  				set lngLevel to lngLevel + 1
  				set end of lstNumbered to {lngLevel, "("}
  			else if strToken is ")" then
  				set end of lstNumbered to {lngLevel, ")"}
  				set lngLevel to lngLevel - 1
  			else
  				set end of lstNumbered to strToken
  			end if
  		end repeat
  		
  		if lngLevel < 0 then
  			set end of lstErrors to ((-lngLevel) as text) & my pl(lngLevel, " unmatched closing bracket")
  		else if lngLevel > 0 then
  			set end of lstErrors to (lngLevel as text) & my pl(lngLevel, " unmatched opening bracket")
  		end if
  		{lstNumbered, lstErrors}
  	end MatchBrackets
  	
  	on CommentFirstTokens(blnObject, blnWhere, blnTestRoom)
  		set str to ""
  		if not blnObject then set str to str & "Specify the kind of object to be found:  projects|tasks|contexts|folders" & return & return
  		if not blnWhere then set str to str & "Follow projects|tasks|contexts|folders with where|whose" & return & return
  		if not blnTestRoom then set str to str & "After where|whose specify a logical test which the objects must pass"
  		str
  	end CommentFirstTokens
  	
  end script
  
  script sSearch
  	property spIsArchive : false
  	property spClass : 0
  	property spFilledQuery : ""
  	property spError : ""
  	
  	on QueryMatches(strQuery)
  		set spIsArchive to false
  		set spClass to 0
  		set spFilledQuery to ""
  		set spError to ""
  		-- CHECK THAT THE QUERY STARTS WITH PROJECTS/TASKS/CONTEXTS/FOLDERS
  		-- STRIP OUT MULTIPLE SPACE
  		-- NORMALIZE "WHOSE" TO "WHERE"
  		-- AND STRIP ANY LEADING VARIANT OF 'ARCHIVED', SETTING A FLAG IF IT WAS PRESENT
  		set {strQuery, spClass, spIsArchive, blnIsRepetition} to PreProcess(strQuery)
  		
  		-- IF all three values false then warn user ...
  		if spClass is 0 then
  			set spError to "First word of search must be 'projects' or 'tasks' or 'contexts' or 'folders'," & return & return & ¬
  				"second word must be 'whose' or 'where'"
  		end if
  		
  		-- IF THE QUERY CONTAINS ANY <dte> <txt> <num> placeholders, prompt for values
  		set spFilledQuery to my FillFields(strQuery)
  		set {blnDate, blnNum, blnTxt} to {spFilledQuery contains "<dte", spFilledQuery contains "<num", spFilledQuery contains "<txt"}
  		-- Exit if placeholders are still unresolved
  		if (blnDate or blnNum or blnTxt) then
  			set spError to "Unresolved place-holder in search:" & return & return & spFilledQuery
  			return
  		end if
  		
  		-- IF THIS IS A REPETITION QUERY PASS IT TO ITERATIVE PROCESS
  		-- (Properties of repetition records are inaccessible from applescript 'where' queries	
  		if blnIsRepetition then
  			set {strScript, spError} to RecPropQuery(spFilledQuery, spClass)
  			if length of strScript < 1 then return
  		else
  			-- OTHERWISE PASS IT THROUGH TO APPLESCRIPT 'WHERE' PROCESSING
  			set strScript to my QueryScript(spFilledQuery)
  		end if
  		
  		-- RUN THE SCRIPT THAT HAS BEEN ASSEMBLED, TO GET
  		-- A REFERENCE TO A SUBSET OF PROJECTS OR TASKS OR CONTEXTS OR FOLDERS
  		try
  			set oScript to run script strScript
  		on error spError
  			set spError to "The search:" & return & return & tab & spFilledQuery & return & return & ¬
  				"lead to the error:" & return & return & tab & spError
  			return
  		end try
  		
  		tell application id "OFOC"
  			set oDoc to my GetDoc(spIsArchive)
  			if oDoc is missing value then
  				activate
  				display dialog "Requested OF document not found ..." with title my pTitle with icon my IconFile()
  				return
  			end if
  		end tell
  		
  		try -- THE SEARCH HAPPENS HERE
  			set my plstObjects to GetObjects(oDoc) of oScript
  			set oScript to 0
  		on error spError
  			set spError to "The search:" & return & return & tab & spFilledQuery & return & return & ¬
  				"lead to the error:" & return & return & tab & spError
  			return
  		end try
  		
  		set blnFound to false
  		try
  			set blnFound to (count of my plstObjects) > 0
  		on error
  			set spError to "search language logic error in:" & return & return & quote & spFilledQuery & quote
  		end try
  	end QueryMatches
  	
  	on QueryScript(strQuery)
  		"
  script
  	on GetObjects(oDoc)
  		tell application id " & quote & "OFOC" & quote & "
  			tell oDoc to	return flattened " & strQuery & "
  		end tell
  	end GetObjects
  end script
  "
  	end QueryScript
  	
  	on RecPropQuery(strQuery, lngClass)
  		set {strClass, strProp, strCondition, strMessage} to ParseRecQuery(strQuery, lngClass)
  		if length of strMessage > 0 then return {"", strMessage}
  		set strSingleClass to text 1 thru -2 of strClass
  		
  		{"
  script
  	property plstID : {}
  	property plstValue : {}
  	
  	on GetObjects(oDoc)
  		tell application id " & quote & "OFOC" & quote & "
  			tell oDoc
  				set refObjects to a reference to flattened " & strClass & "
  				tell refObjects
  					set {my plstID, my plstValue} to {id, " & strProp & "}
  				end tell
  				set lstObj to {}
  				repeat with iObj from 1 to count of plstID
  					set varRec to item iObj of plstValue
  					if varRec is not missing value then
  						if " & strCondition & " then ¬
  							set end of lstObj to " & strSingleClass & " id (item iObj of plstID)
  					end if
  				end repeat
  				return lstObj
  			end tell
  		end tell
  	end GetObjects
  end script
  ", ""}
  	end RecPropQuery
  	
  	on ParseRecQuery(strQuery, lngClass)
  		-- Parse strQuery to yield parts required
  		
  		-- projects where (fixed/steps/unit of repetition/review interval) <op> <val>
  		-- tasks where (fixed/steps/unit of repetition) <op> <val>
  		-- contexts where (altitude/latitude/longitude/name/radius of location) <op> <val>
  		
  		-- Clear any multiple spaces
  		-- 1.  Repetition ? review ? location ? combination ?
  		set blnRepn to (offset of "repetition" in strQuery) > 0
  		set blnRevw to (offset of "review" in strQuery) > 0
  		set blnLocation to (offset of "location" in strQuery) > 0
  		if blnRepn and blnRevw and blnLocation then
  			return {{}, "searches combining review and repetition or location records not currently parseable"}
  		else if not (blnRepn or blnRevw or blnLocation) then
  			return {{}, "not a review/repetition/location record search"}
  		else if blnRepn then
  			set strProp to "repetition"
  		else if blnRevw then
  			set strProp to "review interval"
  		else
  			set strProp to "location"
  		end if
  		
  		set lstClassFlags to {false, false, false, false}
  		set item lngClass of lstClassFlags to true
  		set {blnFolder, blnProject, blnTask, blnContext} to lstClassFlags
  		
  		-- 4. check that the class matches the property
  		if strProp is "repetition" then
  			if blnContext then return {{}, "Contexts do not have repetition records"}
  			if blnFolder then return {{}, "Folders do not have repetition records"}
  		else if strProp is "review interval" then
  			if not blnProject then return {{}, "Only projects have review intervals"}
  		else if strProp is "location" then
  			if not blnContext then return {{}, "Only contexts have location records"}
  		end if
  		
  		-- 5. Adapt the condition for iterative testing
  		set text item delimiters to " where "
  		set lstParts to text items of strQuery
  		set text item delimiters to space
  		
  		set strCondition to item 2 of lstParts
  		set strCondition to my FindReplace(strCondition, strProp, "varRec")
  		
  		set strClass to my IntegerClass(lngClass)
  		return {strClass, strProp, strCondition, ""}
  	end ParseRecQuery
  	
  	
  	on GetValue(strTemplate, strTag, strTagStart)
  		if strTagStart is "<txt" then
  			GetTxt(strTemplate, strTag)
  		else if strTagStart is "<dte" then
  			GetDate(strTemplate, strTag)
  		else if strTagStart is "<num" then
  			GetNum(strTemplate, strTag)
  		end if
  	end GetValue
  	
  	-- PROMPT USER FOR A SEARCH STRING
  	on GetTxt(strClause, strTag)
  		try
  			tell application id "com.apple.systemevents"
  				activate
  				set strTxt to (text returned of (display dialog strClause & return & return & ¬
  					"Enter phrase (no quotes) for " & strTag default answer "" buttons {"OK"} default button {"OK"} with title my pTitle with icon my IconFile()))
  			end tell
  		on error
  			return strTag
  		end try
  		strTxt
  	end GetTxt
  	
  	-- PROMPT USER FOR AN INTEGER
  	on GetNum(strClause, strTag)
  		try
  			tell application id "com.apple.systemevents"
  				activate
  				set strNum to (text returned of (display dialog strClause & return & return & "Enter number for " & strTag default answer ¬
  					"0" buttons {"OK"} default button {"OK"} with title my pTitle with icon my IconFile()))
  			end tell
  			set nNum to strNum as number
  		on error
  			tell application id "com.apple.systemevents"
  				activate
  				display dialog strNum & " :" & return & return & "invalid number - skipping number entry" buttons {"OK"} default button {"OK"} ¬
  					with icon my IconFile() with title pTitle
  			end tell
  			return strTag
  		end try
  		strNum
  	end GetNum
  	
  	-- PROMPT USER FOR A DATE
  	on GetDate(strClause, strTag)
  		set strDefault to short date string of (current date)
  		tell application id "com.apple.systemevents"
  			activate
  			set recReturned to (display dialog strClause & return & return & "Enter date for " & strTag ¬
  				default answer strDefault buttons {"OK"} default button {"OK"} with title pTitle with icon my IconFile())
  			if button returned of recReturned is "cancel" then
  				return strTag
  			else
  				set strDate to (text returned of recReturned)
  			end if
  		end tell
  		-- Check whether this string can be parsed as a date, and reduced to a date "nn/nn/nn" form
  		set strExpression to DateExpression(strDate)
  		if length of strExpression > 0 then
  			return strExpression
  		else
  			tell application id "com.apple.systemevents"
  				activate
  				display dialog strDate & " :" & return & return & "Unrecognized date" buttons {"OK"} default button {"OK"} with title my pTitle with icon my IconFile()
  			end tell
  			return strTag
  		end if
  	end GetDate
  	
  	on DateExpression(strExpression)
  		set strScript to "
  set now to (current date)
  set strDefault to short date string of now
  set today to date strDefault
  set yesterday to today - 1 * days
  set tomorrow to today + 1 * days
  tell application id \"OFOC\"
  	tell default document to set soon to now + ((value of setting id \"DueSoonInterval\") as integer)
  end tell 
  "
  		set strTest to strScript & strExpression
  		-- is it directly parseable by applescript ? (today|yesterday|tomorrow) [+ N * days|weeks]
  		try
  			set oScript to missing value
  			set oScript to run script strTest
  			if class of oScript is date then return strExpression
  		end try
  		
  		-- Does it become parseable to applescript if we prefix "date "  :  date "08/08/1988"
  		set strASDate to "date " & quote & strExpression & quote
  		try
  			set oScript to run script strASDate
  		end try
  		if class of oScript is date then return strASDate
  		
  		set lstTokens to my Tokenize(strExpression)
  		
  		-- Translate any date tag tokens (<jan>, <feb> etc) to applescript date strings
  		set {blnMonthFound, lstTokens} to my ReadDateTags(lstTokens)
  		
  		-- 		Translate any relative date expressions now|today|tomorrow|yesterday|soon
  		set {blnRelvFound, lstTokens} to ReadRelvDates(lstTokens)
  		
  		-- Translate any expressions of the form today +|- Nd|w|m|y   > date ShortDateString
  		set {blnIntervalFound, lstTokens} to my ReadIntervals2(lstTokens)
  		
  		if (blnMonthFound or blnIntervalFound or blnRelvFound) then
  			return my ReString(lstTokens, space)
  		else
  			return ""
  		end if
  	end DateExpression
  	
  	
  	
  	on FillFields(strTemplate)
  		set strFilled to strTemplate
  		set {blnDate, blnNum, blnTxt} to {strTemplate contains "<dte", strTemplate contains "<num", strTemplate contains "<txt"}
  		if not (blnDate or blnNum or blnTxt) then return strTemplate
  		
  		set lngTag to 2
  		set lngRest to 3
  		if blnDate then
  			set lstClauses to BuildClauses(strFilled, "<dte")
  			set strFilled to first item of lstClauses
  			repeat with iSub from 2 to length of lstClauses
  				set lstSub to (item iSub of lstClauses)
  				set strFilled to strFilled & (item lngTag of lstSub) & (item lngRest of lstSub)
  			end repeat
  		end if
  		
  		if blnNum then
  			set lstClauses to BuildClauses(strFilled, "<num")
  			set strFilled to first item of lstClauses
  			repeat with iSub from 2 to length of lstClauses
  				set lstSub to (item iSub of lstClauses)
  				set strFilled to strFilled & (item lngTag of lstSub) & (item lngRest of lstSub)
  			end repeat
  		end if
  		
  		if blnTxt then
  			set lstClauses to BuildClauses(strFilled, "<txt")
  			set strFilled to first item of lstClauses
  			repeat with iSub from 2 to length of lstClauses
  				set lstSub to (item iSub of lstClauses)
  				set strFilled to strFilled & quote & (item lngTag of lstSub) & quote & (item lngRest of lstSub)
  			end repeat
  		end if
  		strFilled
  	end FillFields
  	
  	on BuildClauses(strTemplate, strTagStart)
  		set lstSubClauses to Segment(strTemplate, strTagStart)
  		set lngClauses to length of lstSubClauses
  		set lngTag to 2
  		repeat with iSub from 2 to lngClauses
  			set refiSub to (a reference to (item iSub of lstSubClauses))
  			if not (first item of refiSub) then
  				set strTag to item lngTag of refiSub
  				set strValue to GetValue(strTemplate, strTag, strTagStart)
  				-- Apply any value to this clause and any others with the same tag
  				if strValue is not strTag then
  					repeat with iRest from iSub to lngClauses
  						set refiRest to (a reference to item iRest of lstSubClauses)
  						if not (first item of refiRest) then
  							if (item lngTag of refiRest) is strTag then ¬
  								set {item lngTag of refiRest, first item of refiRest} to {strValue, true}
  						end if
  					end repeat
  				end if
  			end if
  		end repeat
  		lstSubClauses
  	end BuildClauses
  	
  	-- SEGMENT A TEMPLATE QUERY SO THAT TAGS CAN BE REPLACE WITH DATA VALUES
  	-- Return a list of {blnFilled, tag, strOpandVal} pairs (preceded by start of string)
  	-- tag is e.g. <dte | <txt | <num
  	on Segment(strClause, strTag)
  		set strDelim to text item delimiters
  		set text item delimiters to strTag
  		set lstParts to text items of strClause
  		set lstSegments to {first item of lstParts}
  		
  		repeat with iPart from 2 to length of lstParts
  			set strPart to item iPart of lstParts
  			set lngClose to offset of ">" in strPart
  			if lngClose > 0 then
  				set strToken to strTag & (text 1 thru lngClose of strPart)
  			else
  				set strToken to strTag
  			end if
  			if lngClose < length of strPart then
  				set end of lstSegments to {false, strToken, text from (lngClose + 1) to end of strPart}
  			else
  				set end of lstSegments to {false, strToken, ""}
  			end if
  		end repeat
  		set text item delimiters to strDelim
  		lstSegments
  	end Segment
  	
  	-- Verify that first word is projects | tasks | contexts | folders
  	-- and that second word is whose | where
  	on PreProcess(strQuery)
  		
  		set lstTokens to my Tokenize(strQuery)
  		
  		set lngTokens to length of lstTokens
  		if lngTokens < 1 then return {"", 0, false, false}
  		
  		set {{blnArchive, blnObject, blnWhere, blnTestRoom}, lstTokens} to my CheckFirstTokens(lstTokens)
  		if blnArchive then
  			set lstTokens to items 2 thru end of lstTokens
  			set lngTokens to lngTokens - 1
  		end if
  		
  		set blnQuery to (blnObject and blnWhere and blnTestRoom) or (blnObject and (lngTokens is 1))
  		if not blnQuery then return {"", 0, false, false}
  		
  		set strClass to first item of lstTokens
  		set lngClass to my ClassInteger(strClass)
  		
  		-- Expand any abbreviations for this class which are listed in our Abbrevns dictionary
  		set {lstTokens, blnDelta} to ExpandAbbrevns(lstTokens, strClass)
  		-- In case any expansions have introduced new token boundaries, we need to restring and retokenize here
  		if blnDelta then
  			set strQuery to my ReString(lstTokens, space)
  			set lstTokens to my Tokenize(strQuery)
  		end if
  		
  		if blnWhere then
  			-- nomalize "whose" to "where"
  			if lngTokens > 1 then set item 2 of lstTokens to "where"
  			
  			set lstNoBrackets to my DeBracket(lstTokens)
  			set lngTokens to (count of lstNoBrackets)
  			
  			--Check for RECORD queries (repetition or location)
  			if lngTokens > 4 then
  				set blnIsRec to ContainsAny(strQuery, {"unit of", "fixed of", "steps of", "location"})
  			else
  				set blnIsRec to false
  			end if
  			
  			-- Translate any date tag tokens (<jan>, <feb> etc) to applescript date strings
  			set {blnFound, lstTokens} to my ReadDateTags(lstTokens)
  			
  			-- 	Translate any relative date expressions now|today|tomorrow|yesterday|soon
  			set {blnFound, lstTokens} to ReadRelvDates(lstTokens)
  			
  			-- Translate any expressions of the form today +|- Nd|w|m|y   > date ShortDateString
  			set {blnFound, lstTokens} to my ReadIntervals2(lstTokens)
  			
  			set text item delimiters to space
  			set strNormalized to my ReString(lstTokens, space)
  			return {strNormalized, lngClass, blnArchive, blnIsRec}
  		else
  			return {strClass, lngClass, blnArchive, false}
  		end if
  	end PreProcess
  	
  	on ContainsAny(str, lst)
  		repeat with i from 1 to length of lst
  			if item i of lst is in str then return true
  		end repeat
  		false
  	end ContainsAny
  	
  	-- Translate any <tags> (starting and ending with angle-brackets) 
  	-- (other than <dte...> <txt...> <num...> which will be resolved interactively)
  	on ExpandAbbrevns(lstTokens, strClass)
  		set blnDelta to false
  		set lstAbbrevns to my GetAbbrevns(strClass, true)
  		
  		repeat with iToken from 1 to length of lstTokens
  			set strToken to (item iToken of lstTokens) as string
  			if first character of strToken = "[" then
  				if last character of strToken = "]" then
  					
  					repeat with oAbbrevn in lstAbbrevns
  						set {strAbbrevn, strExpansion} to oAbbrevn
  						if strToken is strAbbrevn then
  							set item iToken of lstTokens to strExpansion
  							set blnDelta to true
  							exit repeat
  						end if
  					end repeat
  					
  				end if
  			end if
  		end repeat
  		return {lstTokens, blnDelta}
  	end ExpandAbbrevns
  	
  end script
  
  script sDisplay
  	property spIsInGUI : false
  	
  	on OFGUIShow(blnIsArchive, strFilledQuery, lngClass, strComment)
  		set lngMatches to length of my plstObjects
  		if lngMatches < 1 then
  			set spIsInGUI to true
  			return
  		end if
  		
  		set lstOptions to GetOptions(strComment)
  		set {lstScriptOptions, lstDropDown} to lstOptions
  		set {blnShortCut, blnNewWindow, blnWarnHidden} to lstScriptOptions
  		
  		-- OPTIONALLY LIST MATCHES FOR MANUAL SELECTION OF SUBSET
  		if not blnShortCut then
  			if not ChooseFromHarvest(blnIsArchive, strFilledQuery) then -- User chose not to select objects
  				set spIsInGUI to false
  				return
  			end if
  		end if
  		
  		tell application "OmniFocus"
  			-- GET A NEW OR EXISTING WINDOW FOR THE DOCUMENT CONTAINING THE MATCHES
  			set oWin to my ClearDocWin(blnIsArchive, blnNewWindow)
  			if oWin is missing value then
  				activate
  				display dialog "Could not get OF document window" buttons {"OK"} with title my pTitle
  				set spIsInGUI to true
  				return
  			end if
  			set lngWin to id of oWin
  			if my pblnHideWindow then set visible of oWin to false -- normally skipped
  			set oWin to window id lngWin
  			
  			
  			-- SET FLAGS FOR THE CLASS OF THE OBJECTS TO DISPLAY
  			set lstClassFlags to {false, false, false, false}
  			if lngClass > 0 then
  				set item lngClass of lstClassFlags to true
  			else
  				activate
  				display dialog "Unknown class type 0 in Display's OFGUIShow()"
  				set spIsInGUI to false
  				return
  			end if
  			set {blnIsFolders, blnIsProjects, blnIsTasks, blnIsContexts} to lstClassFlags
  			
  			-- SET THE VIEW MODE TO PROJECT/CONTEXT
  			set blnContextView to blnIsContexts
  			tell oWin
  				set strView to selected view mode identifier
  				if blnContextView then
  					if strView is not "context" then set selected view mode identifier to "context"
  				else
  					if strView is not "project" then set selected view mode identifier to "project"
  				end if
  				
  				-- ESTABLISH SUBSET OF FOLDERS/PROJECTS/CONTEXTS FOR FOCUS/SELECTION IN SIDEBAR
  				-- SET ANY FOCUS THAT IS REQUIRED
  				set {blnTreeOne, my plstParents} to {false, {}}
  				if blnIsContexts then
  					set focus to {}
  				else if (blnIsFolders or blnIsProjects) then
  					set focus to my plstObjects
  				else -- TASKS: 
  					-- 	GATHER ANY LISTS OF PARENT (PROJECTS/CONTEXTS)
  					--	AND SET A FLAG FOR SUBSEQUENT SELECTION OF TREE 1 (INBOX / NO CONTEXTS)
  					if blnContextView then -- PREPARE TO SELECT PARENT CONTEXTS (AND PERHAPS TREE 1)
  						set focus to {}
  						repeat with oTask in my plstObjects
  							set oContext to context of oTask
  							if oContext is missing value then
  								set blnTreeOne to true
  							else
  								set end of my plstParents to oContext
  							end if
  						end repeat
  					else -- PREPARE TO FOCUS ON (AND SELECT) PARENT PROJECTS (AND PERHAPS SELECT TREE 1)
  						repeat with oTask in my plstObjects
  							set oProj to containing project of oTask
  							if oProj is missing value then
  								set blnTreeOne to true
  							else
  								set end of my plstParents to oProj
  							end if
  						end repeat
  						set focus to my plstParents
  					end if
  				end if
  			end tell -- oWin
  			
  			-- RECORD, AND THEN CLEAR, THE CURRENT FILTERS
  			set lstOldFilters to my GetFilters(oWin)
  			my SetFilters(oWin, {"", "-", "-", "", "", ""}, blnContextView, strComment) -- no need to clear sorting and grouping ...
  			
  			-- MAKE SELECTIONS IN SIDEBAR
  			my MakeSelns(oWin, true, blnIsTasks, blnTreeOne)
  			-- (IF THIS IS A TASK SEARCH) MAKE SELECTIONS IN CONTENTS PANEL 
  			if blnIsTasks then my MakeSelns(oWin, false, blnIsTasks, blnTreeOne)
  			
  			-- DERIVE NEW FILTER SETTINGS BY APPLYING ANY INSTRUCTION TO RECORDED SETTINGS
  			set {strSmartGroup, strGrouping, strSorting, strState, strFlagged, strDurn} ¬
  				to my ExistingPlusRequests(lstOldFilters, lstDropDown)
  			
  			-- IMPOSE NEW FILTER SETTINGS ONE BY ONE (COUNTING HIDDEN MATCHES IF REQUIRED)
  			set {iSidebar, iState, iFlagged, iDuration} to {1, 2, 3, 4}
  			set lstFilterNames to {"Sidebar", "State", "Flagged", "Duration"}
  			if blnWarnHidden then
  				set lstHidden to {0, 0, 0, 0}
  				set lngMatches to count of my plstObjects
  			end if
  			set oContent to content of oWin
  			
  			set lngRemaining to lngMatches
  			if (strSmartGroup is not "") then
  				-- 		SET SIDEBAR FILTER
  				if blnContextView then
  					set strClass to "-contexts"
  				else
  					set strClass to "-projects"
  				end if
  				set oSidebar to sidebar of oWin
  				set lngSelns to count of selected trees of oContent
  				set selected smart group identifier of oSidebar to strSmartGroup & strClass
  				
  				-- 		MAKE ANY REQUIRED RECORDING OF MATCHES HIDDEN BY SIDEBAR FILTER
  				if blnWarnHidden then
  					if blnIsTasks then
  						set lngSelns to count of selected trees of oContent
  					else
  						set lngSelns to count of selected trees of oSidebar
  						set oSidebar to missing value
  						if blnTreeOne then set lngSelns to (lngSelns - 1)
  					end if
  					set lngHidden to lngMatches - lngSelns
  					
  					if lngHidden > 0 then
  						set lngRemaining to lngMatches - lngHidden
  						set item iSidebar of lstHidden to lngHidden
  					end if
  				end if
  			end if
  			-- 		SET {STATUS, FLAGGED, DURATION} FILTERS (RECORDING ANY CONCEALMENTS, IF REQUIRED)
  			tell oContent
  				set lstFilters to {strSmartGroup, strState, strFlagged, strDurn}
  				repeat with iFilter from iState to iDuration -- (2 to 4)
  					set strValue to item iFilter of lstFilters as string
  					if (strValue is not "") then
  						if iFilter is iState then
  							set selected task state filter identifier to strValue
  						else if iFilter is iFlagged then
  							set selected task flagged filter identifier to strValue
  						else
  							set selected task duration filter identifier to strValue
  						end if
  						set blnCounting to (blnWarnHidden and blnIsTasks)
  						if blnCounting then
  							set lngSelns to count of selected trees
  							set lngHidden to lngRemaining - lngSelns
  							
  							if lngHidden > 0 then
  								set lngRemaining to lngRemaining - lngHidden
  								set item (iFilter) of lstHidden to lngHidden
  							end if
  						end if
  					end if
  				end repeat
  				
  				if blnWarnHidden then
  					-- list any filters which are concealing matches, with the number of matches involved
  					set lngMax to 0
  					set lngTotal to 0
  					repeat with oHidden in lstHidden
  						set lng to contents of oHidden
  						set lngTotal to lngTotal + lng
  						if lng > lngMax then set lngMax to lng
  					end repeat
  					if lngMax > 0 then
  						set strClass to (class of first item of my plstObjects) as string
  						set lngDigits to length of (lngMax as string)
  						set lstBlockers to {}
  						set strMod to ""
  						repeat with iFilter from iSidebar to iDuration
  							set lngHidden to contents of item iFilter of lstHidden
  							if lngHidden > 0 then
  								set str to my PadNum(lngHidden, lngDigits) & tab & strMod & my pl(lngHidden, strClass) & " hidden by: " & ¬
  									item iFilter of lstFilterNames & "=" & item iFilter of lstFilters
  								set end of lstBlockers to str
  								set strMod to "more "
  							end if
  						end repeat
  						tell application id "com.apple.systemevents"
  							activate
  							set varChoice to choose from list lstBlockers default items (lstBlockers) with title my pTitle ¬
  								with prompt "HIDDEN MATCHES" & return & return & "Cmd-A to select all," & return & ¬
  								"or Cmd-Click for particular selections" & return & return & (lngTotal as string) & " matches for: " & return & return & strFilledQuery & ¬
  								return & return & "are currently hidden by the following filter settings:" OK button name {"CLEAR filters selected above"} cancel button name ¬
  								"Leave filter settings unchanged" with multiple selections allowed
  						end tell
  						if varChoice is not false then
  							
  							set lstNewSetting to {strSmartGroup, "-", "-", strState, strFlagged, strDurn}
  							repeat with oHidden in varChoice
  								set my text item delimiters to ": "
  								set lstParts to text items of oHidden
  								set my text item delimiters to "="
  								set lstParts to text items of last item of lstParts
  								set strFilter to first item of lstParts
  								if strFilter is "Sidebar" then
  									set item 1 of lstNewSetting to ""
  								else if strFilter is "State" then
  									set item 4 of lstNewSetting to ""
  								else if strFilter is "Flagged" then
  									set item 5 of lstNewSetting to ""
  								else if strFilter is "Duration" then
  									set item 6 of lstNewSetting to ""
  								else
  									tell application id "com.apple.systemevents"
  										activate
  										display dialog "Hidden matches: request to clear unknown setting:" & strFilter
  									end tell
  								end if
  							end repeat
  							set my text item delimiters to space
  							
  							-- CLEAR ALL FILTERS AGAIN
  							my SetFilters(oWin, {"", "-", "-", "", "", ""}, blnContextView, strComment)
  							-- Renew selections
  							
  							-- 	REPEAT SELECTIONS
  							-- 		MAKE SELECTIONS IN SIDEBAR
  							my MakeSelns(oWin, true, blnIsTasks, blnTreeOne)
  							-- 		(IF THIS IS A TASK SEARCH) MAKE SELECTIONS IN CONTENTS PANEL 
  							if blnIsTasks then my MakeSelns(oWin, false, blnIsTasks, blnTreeOne)
  							
  							-- REIMPOSE EASED FILTERS
  							my SetFilters(oWin, lstNewSetting, blnContextView, strComment)
  						end if
  					end if
  				end if
  				-- 	FINALLY SET SORTING AND GROUPING OPTIONS
  				my SetFilters(oWin, {"-", strGrouping, strSorting, "-", "-", "-"}, blnContextView, strComment)
  			end tell
  			-- AND RESTORE VISIBILITY OF THE WINDOW, if necessary
  			if not visible of oWin then set visible of oWin to true
  		end tell
  		set {oContent, oWin} to {missing value, missing value}
  		set spIsInGUI to true
  	end OFGUIShow
  	
  	on GetOptions(strComments)
  		if length of strComments < 1 then
  			set strFirstComments to ""
  		else
  			set text item delimiters to "--"
  			set strFirstComments to second text item of strComments
  			set text item delimiters to space
  		end if
  		return ParseComments(strFirstComments)
  	end GetOptions
  	
  	on CheckIdentifier(strIdentifier, strSetting, strComments)
  		tell application id "com.apple.systemevents"
  			activate
  			display dialog "Unknown " & strIdentifier & " identifier: " & return & return & quote & strSetting & quote & " in:" & return & return & strComments & ¬
  				return & return & "Correct, (e.g. using assisted query-editing) and try again." buttons {"OK"} with icon my IconFile() with title my pTitle
  		end tell
  	end CheckIdentifier
  	
  	on ChooseFromHarvest(blnIsArchive, strQuery)
  		-- DISPLAY NUMBERED LIST OF RESULTS (AND ALLOW CHOICE OF SUBSET)
  		set iObj to 1
  		set lngDigits to (length of ((length of my plstObjects) as string)) -- (delta:20100719)
  		
  		--set strCancel to my PadNum(0, lngDigits) & tab & "(Do not view any of these matches in Omnifocus)"
  		set lstIndex to {}
  		repeat with oObj in my plstObjects
  			set end of lstIndex to my PadNum(iObj, lngDigits) & tab & name of oObj
  			set iObj to iObj + 1
  		end repeat
  		
  		if length of lstIndex > 0 then
  			set lstDefault to first item of lstIndex
  		else
  			set lstDefault to {}
  		end if
  		-- Prepare report of matches for query
  		set text item delimiters to space
  		set lstParts to text items of strQuery
  		set strClass to first item of lstParts
  		set strClass to text 1 thru -2 of strClass
  		
  		set lngMatches to (count of lstIndex)
  		if blnIsArchive then set strClass to "archived " & strClass
  		set strMatchCount to (lngMatches as string) & " " & my pl(lngMatches, strClass)
  		set lngParts to length of lstParts
  		if lngParts > 1 then
  			set strQuery to text items 2 thru lngParts of strQuery as string
  		else
  			set strQuery to ""
  		end if
  		
  		if lngMatches > 1 then
  			set strMultiple to "(Cmd-Click for multiple selections, Ctrl A to select all)" & return & return
  		else
  			set strMultiple to ""
  		end if
  		tell application id "com.apple.systemevents"
  			activate
  			set varChoice to choose from list lstIndex default items lstDefault with title my pTitle ¬
  				with prompt "Manually select matches to view in OmniFocus:" & return & return & ¬
  				strMultiple & strMatchCount & " " & strQuery ¬
  				OK button name {"OK"} cancel button name {"Cancel"} with multiple selections allowed
  		end tell
  		
  		-- IF SUBSET RATHER THAN FULL LIST CHOSEN, 
  		-- PRUNE LIST OF OBJECTS DOWN TO THE SPECIFIED SUBSET
  		if varChoice is false then return false -- ESC
  		set lngObjects to length of my plstObjects
  		set lngChoices to length of varChoice
  		if (lngChoices = 1) then if varChoice = lstDefault then return false -- Do not select anything
  		if lngChoices < lngObjects then
  			-- gather subset of my plstObjects by their indices
  			set lstSelected to {}
  			set my text item delimiters to tab
  			repeat with oLine in varChoice
  				set iIndex to (first item of text items of oLine) as integer
  				set end of lstSelected to item iIndex of my plstObjects
  			end repeat
  			set my text item delimiters to space
  			set my plstObjects to lstSelected
  		end if
  		true
  	end ChooseFromHarvest
  	
  	-- Get a reference to a visible front document window
  	-- and clear any search term
  	on ClearDocWin(blnIsArchive, blnNewWindow)
  		tell application id "OFOC"
  			set oDoc to my GetDoc(blnIsArchive)
  			tell oDoc
  				set refDocWins to a reference to document windows
  				if ((count of refDocWins) < 1) or blnNewWindow then
  					set oWin to make new document window -- with properties {visible:false}
  				else
  					set oWin to first item of refDocWins
  				end if
  			end tell
  			
  			repeat with iWait from 1 to 10
  				try
  					set lngID to id of oWin
  					exit repeat
  				on error
  					delay 0.2
  				end try
  			end repeat
  			
  			-- Bring the window to the front by toggling its visibility
  			try
  				tell window id lngID
  					if its index > 1 then
  						set its visible to false
  						set its visible to true
  					else
  						if its visible is false then set its visible to true
  					end if
  				end tell
  			on error
  				return missing value
  			end try
  			
  			-- NOTE: oWin reference is lost if visible has been switched from false to true
  			set oWin to front document window of oDoc
  			tell oWin
  				set varTerm to search term
  				if varTerm is not missing value then
  					if length of varTerm > 0 then set search term to ""
  				end if
  			end tell
  			return oWin
  		end tell
  	end ClearDocWin
  	
  	on SetFilters(oWin, {strSmartGroup, strGrouping, strSorting, strState, strFlagged, strDurn}, blnContextView, strComment)
  		if blnContextView then
  			set strClass to "-contexts"
  		else
  			set strClass to "-projects"
  		end if
  		tell application id "OFOC"
  			tell oWin
  				if strSmartGroup is not "" then
  					if strSmartGroup is not "-" then if selected smart group identifier of sidebar is not strSmartGroup then ¬
  						set selected smart group identifier of sidebar to strSmartGroup & strClass
  				else
  					set selected smart group identifier of sidebar to "all" & strClass
  				end if
  				tell content
  					try
  						if strGrouping is not "" then
  							if strGrouping is not "-" then if selected grouping identifier is not strGrouping then ¬
  								set selected grouping identifier to strGrouping
  						else
  							set selected grouping identifier to "none"
  						end if
  					on error
  						my CheckIdentifier("grouping", strGrouping, strComment)
  					end try
  					
  					try
  						if strSorting is not "" then
  							if strSorting is not "-" then if selected sorting identifier is not strSorting then ¬
  								set selected sorting identifier to strSorting
  						else
  							if blnContextView then
  								set selected sorting identifier to "context"
  							else
  								set selected sorting identifier to "none"
  							end if
  						end if
  					on error
  						my CheckIdentifier("sorting", strSorting, strComment)
  					end try
  					
  					try
  						if strState is not "" then
  							if strState is not "-" then if selected task state filter identifier is not strState then ¬
  								set selected task state filter identifier to strState
  						else
  							set selected task state filter identifier to "all"
  						end if
  					on error
  						my CheckIdentifier("task state filter", strState, strComment)
  					end try
  					
  					try
  						if strFlagged is not "" then
  							if strFlagged is not "-" then if selected task flagged filter identifier is not strFlagged then ¬
  								set selected task flagged filter identifier to strFlagged
  						else
  							set selected task flagged filter identifier to "all"
  						end if
  					on error
  						my CheckIdentifier("task flagged filter", strFlagged, strComment)
  					end try
  					
  					try
  						if strDurn is not "" then
  							if strDurn is not "-" then if selected task duration filter identifier is not strDurn then ¬
  								set selected task duration filter identifier to strDurn
  						else
  							set selected task duration filter identifier to "any"
  						end if
  					on error
  						my CheckIdentifier("task duration filter", strDurn, strComment)
  					end try
  				end tell
  			end tell
  		end tell
  	end SetFilters
  	
  	-- SELECT OBJECTS IN SIDEBAR OR CONTENT PANEL
  	on MakeSelns(oWin, blnSidebar, blnIsTasks, blnTreeOne)
  		tell application "OmniFocus"
  			activate
  			-- 	GET A REFERENCE TO THE RELEVANT PANEL
  			if blnSidebar then
  				set oPanel to sidebar of oWin
  			else
  				set oPanel to content of oWin
  			end if
  			
  			-- EXPAND ALL TREES
  			set refCollapsed to a reference to (descendant trees of oPanel where (expanded is false and (trees of trees) is not trees))
  			set lstExpanded to expanded of refCollapsed
  			if length of lstExpanded > 0 then set expanded of refCollapsed to true
  			
  			-- MAKE THE RELEVANT SELECTIONS
  			-- INCLUDING TREE 1 (INBOX / NO CONTEXT) IF REQUIRED
  			tell oPanel
  				select {}
  				if blnSidebar then
  					if blnIsTasks then
  						try
  							select my plstParents
  						end try
  						set lngTrees to count of selected trees
  						set lngParents to count of plstParents
  						repeat with iTick from 1 to 5
  							if lngTrees ≤ lngParents then
  								delay 0.5
  								try
  									select my plstParents
  								end try
  								set lngTrees to count of selected trees
  								if lngTrees = lngParents then exit repeat
  							end if
  						end repeat
  						if blnTreeOne then select tree 1 with extending
  					else
  						select my plstObjects
  					end if
  				else
  					try
  						select my plstObjects
  					end try
  					set lngTrees to count of selected trees
  					set lngObjects to count of plstObjects
  					repeat with iTick from 1 to 5
  						if lngTrees ≤ lngObjects then
  							delay 0.5
  							try
  								select my plstObjects
  							end try
  							set lngTrees to count of selected trees
  							if lngTrees = lngObjects then exit repeat
  						end if
  					end repeat
  				end if
  			end tell
  		end tell
  	end MakeSelns
  	
  	-- Combine filter setting requests with existing filter state
  	on ExistingPlusRequests(lstOldFilters, lstDropDown)
  		set lngDropDown to length of lstDropDown
  		repeat with iSetting from 1 to lngDropDown
  			set strSetting to item iSetting of lstDropDown
  			if strSetting is equal to "-" then ¬
  				set item iSetting of lstDropDown to item iSetting of lstOldFilters
  		end repeat
  		lstDropDown
  	end ExistingPlusRequests
  	
  	on GetFilters(oWin)
  		tell application "OmniFocus"
  			set strSmartGroup to selected smart group identifier of sidebar of oWin
  			set my text item delimiters to "-"
  			set lstParts to text items of strSmartGroup
  			set strSmartGroup to (items 1 thru ((length of lstParts) - 1) of lstParts) as string
  			set my text item delimiters to space
  			tell content of oWin
  				{strSmartGroup, ¬
  					selected grouping identifier, selected sorting identifier, ¬
  					selected task state filter identifier, selected task flagged filter identifier, ¬
  					selected task duration filter identifier}
  			end tell
  		end tell
  	end GetFilters
  	
  	on PadNum(lngNum, lngDigits)
  		set strNum to lngNum as string
  		set lngGap to (lngDigits - (length of strNum))
  		repeat while lngGap > 0
  			set strNum to "0" & strNum
  			set lngGap to lngGap - 1
  		end repeat
  		strNum
  	end PadNum
  end script
  