Window
The Window class contains the DocumentWindow subclass that displays the controls and views for accessing and altering the data in the OmniFocus backing database.
NOTE: Beginning with OmniFocus v4.2:
- Window.selectForecastDays() now supports multiple days on all platforms, not just macOS.
- Window.isCompact (previously iOS-only), Window.isTab, and Window.tabGroupWindows (previously Mac-only) are now implemented across all platforms (even if their results are constant on some platforms).
The scripting implementation for the Window class contains no properties, and one function.
close() • Close the document window: document.windows[0].close()
Close All But Front Window
if (document.windows.length > 1){
var winCount = document.windows.length
for (var i = winCount - 1; i >= 1; i--) {
document.windows[i].close()
}
}
DocumentWindow Instance Properties
Here are the properties of the DocumentWindow class:
content (ContentTree or null r/o) • (macOS) The tree of nodes representing the content area of the window.
focus (Array of Project or Folder or null) • The Folders and Projects that the window is focusing on, limiting the sidebar to show only these items. To unfocus, provide an empty array as the value: [ ]
inspectorVisible (Boolean) • (v4.+) Whether the inspector is currently visible in the window. On iOS, showing this pane may implicitly hide other panes and may be only transiently visible, depending on the available space.
isCompact (Boolean r/o) • Whether the window is in compact layout, where extra panes like the sidebar and inspector are shown atop the content instead of side-by-side.
isTab (Boolean r/o) • Whether or not this window is a tab. This is not available on iOS or iPadOS.
perspective (Perspective.BuiltIn or Perspective.Custom or null) • The perspective displayed in the window.
selection (Selection r/o) • The window selection object (see next section for details)
sidebar (SidebarTree or null r/o) • (macOS) The tree of nodes representing the sidebar of the window.
sidebarVisible (Boolean) • (v4.+) Whether the sidebar is currently visible in the window. On iOS, showing this pane may implicitly hide other panes and may be only transiently visible, depending on the available space.
tabGroupWindows (Array of DocumentWindow r/o) • (macOS) The array of sibling Window objects that are in tabs alongside this Window. If isTab is false, then this will return an array that solely contains this Window. This is not available on iOS or iPadOS.
toolbarVisible (Boolean) • (v4.5) Whether the toolbar is currently visible in the window. This only returns false on macOS; the toolbar is always visible on iOS and visionOS.
Window Panel Visibility
function showWindowPanels(shouldShow){
window = document.windows[0]
window.sidebarVisible = shouldShow
window.inspectorVisible = shouldShow
}
showWindowPanels(false)
The “name” of an OmniFocus window is the name of the perspective it currently displays. Here’s a script that gets the names of the open OmniFocus windows:
Names of Windows
winNames = document.windows.map(win => win.perspective.name)
A script that sets the value of the focus property:
Setting Window Focus
project1 = projectNamed("My Top-Level Project")
project2 = projectNamed("Single-Action Project")
document.windows[0].focus = [project1, project2]
A script that sets the value of the perspective property:
Get | Set Perspective
// get the current perspective object
document.windows[0].perspective
//--> [object Perspective.BuiltIn: Projects]
// set the perspective
document.windows[0].perspective = Perspective.BuiltIn.Inbox
A script that sets the values of the perspective and focus properties:
Focus Dropped Projects
var dropped = new Array()
library.apply(item => {
if (item instanceof Project && item.status === Project.Status.Dropped){
dropped.push(item)
}
})
if(dropped.length > 0){
document.windows[0].perspective = Perspective.BuiltIn.Projects
document.windows[0].focus = dropped
}
And a version of the previous script using the JavaScript filter(…) function with the flattenedProjects property of the Database class:
Focus Dropped Projects
var dropped = flattenedProjects.filter(proj => {
return proj.status === Project.Status.Dropped
})
if(dropped.length > 0){
document.windows[0].perspective = Perspective.BuiltIn.Projects
document.windows[0].focus = dropped
}
A script that will focus only projects that have been tagged with the indicated tags:
Focus Tagged Projects
tag1 = flattenedTags.byName("Weekend") || new Tag("Weekend")
tag2 = flattenedTags.byName("Hiking") || new Tag("Hiking")
matched = flattenedProjects.filter(project => {
tgs = project.tags
return tgs.includes(tag1) && tgs.includes(tag2)
})
if(matched.length > 0){
document.windows[0].focus = matched
}
NOTE: the value of the content and sidebar properties of the Window class are ContentTree and SidebarTree types that inherit from the Tree class, which is the same class that is used to organize and control the display of items in OmniOutliner. As of OmniFocus 4, these node trees are implemented macOS, as well as iOS and iPadOS.
Select Currently Displayed Tasks
var windowContent = document.windows[0].content
var nodes = new Array()
windowContent.rootNode.apply(item => {
if (item.object instanceof Task) {
nodes.push(item)
}
})
if(nodes.length > 0){
windowContent.select(nodes)
}
Here's a plug-in based upon the previous script that will select all of the tasks and/or projects in the current content view. The plug-in includes an action form for displaying a dialog from which the user can choose which type of object to select:
Select All Items in Content View
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.select-all-visible-items",
"version": "1.1",
"description": "This action will select all of the tasks and or projects in the current content view.",
"label": "Select Items in View",
"shortLabel": "Select Items",
"paletteLabel": "Select Items",
"image": "square.stack.3d.up"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
try {
tasksCheckbox = new Form.Field.Checkbox(
"shouldSelectTasks",
"Tasks",
false
)
projectsCheckbox = new Form.Field.Checkbox(
"shouldSelectProjects",
"Projects",
false
)
inputForm = new Form()
inputForm.addField(tasksCheckbox)
inputForm.addField(projectsCheckbox)
inputForm.validate = function(formObject){
shouldSelectTasks = formObject.values['shouldSelectTasks']
shouldSelectProjects = formObject.values['shouldSelectProjects']
return ((shouldSelectTasks || shouldSelectProjects) ? true:false)
}
formObject = await inputForm.show("Select in content view:","Continue")
shouldSelectTasks = formObject.values['shouldSelectTasks']
shouldSelectProjects = formObject.values['shouldSelectProjects']
items = new Array()
document.windows[0].content.rootNode.apply((item) => {
if (shouldSelectTasks && item.object instanceof Task) {
items.push(item)
}
if (shouldSelectProjects && item.object instanceof Project) {
items.push(item)
}
})
if(items.length > 0){
document.windows[0].content.select(items)
}
}
catch(err){
if (!err.causedByUserCancelling){
new Alert(err.name, err.message).show()
}
}
});
action.validate = function(selection, sender){
return true
};
return action;
})();
|
Instance Functions
Here are the instance functions for the Window class:
selectObjects(objects:Array of DatabaseObject) → ( ) • Clears the current selection and then selects the given objects, if present in the current perspective of this window. On iOS, if objects contains more than one object, this will put the outline view into edit mode to accomodate multiple selection.
forecastDayForDate(date:Date) → (ForecastDay) • Returns a ForecastDay object that encompasses date. This will throw an error if Forecast is not the current perspective in this window.
selectForecastDays(days:Array of ForecastDay) → ( ) • Selects the days in the Forecast picker represented by days. On iOS, only the first day is selected, and the rest are ignored. This will throw an error if Forecast is not the current perspective in this window.
IMPORTANT: Since the use these functions require a specific perspective be displayed, the example scripts may occasionally incorporate the use of a Timer function to allow the window time to change views.
The following script uses the flattenedProjects property of the Database class and the byName() and selectObjects() function to select the first project whose name matches the specified name:
Select 1st Project with Specified Name
var targetName = "Project A"
var targetProject = flattenedProjects.byName(targetName)
if(targetProject){
document.windows[0].perspective = Perspective.BuiltIn.Projects
document.windows[0].selectObjects([targetProject])
}
NOTE: The following forecast example scripts use properties and functions of the shared Calendar and DateComponents classes to calculate dates.
Here’s a script that will select the forecast day seven (7) days form today:
Select Forecast for 7th Day from Today
document.windows[0].perspective = Perspective.BuiltIn.Forecast
now = new Date()
cal = Calendar.current
today = cal.startOfDay(now)
dc = new DateComponents()
dc.day = 7
targetDate = cal.dateByAddingDateComponents(today,dc)
fday = document.windows[0].forecastDayForDate(targetDate)
document.windows[0].selectForecastDays([fday])
The following example script demonstrates how to select a range of forecast days:
Select Range of Forecast Days
document.windows[0].perspective = Perspective.BuiltIn.Forecast
now = new Date()
cal = Calendar.current
today = cal.startOfDay(now)
dc = new DateComponents()
fdays = new Array()
var fday, targetDate, i
for (i = 0; i < 7; i++) {
dc.day = i + 1
targetDate = cal.dateByAddingDateComponents(today,dc)
fday = document.windows[0].forecastDayForDate(targetDate)
fdays.push(fday)
}
document.windows[0].selectForecastDays(fdays)
Document Selection
A document’s Selection object belongs to the Window class, which in turn, belongs to the parent implied document. The Selection class includes properties whose values are arrays of references to the specified object types selected in the window: projects, folders, tags, tasks, and a special property for returning all selected database objects (non-interface elements): databaseObjects
Document > Window > Selection
Selection Properties
document.windows[0].selection.database
document.windows[0].selection.databaseObjects
document.windows[0].selection.folders
document.windows[0].selection.projects
document.windows[0].selection.tags
document.windows[0].selection.tasks
document.windows[0].selection.window
document.windows[0].selection.allObjects
For example, here’s a simple script for getting the titles of the selected tasks:
Get Names of Selected Tasks
var selectedTasks = document.windows[0].selection.tasks
var taskTitles = selectedTasks.map(task => task.name)
console.log(taskTitles)
This script will move the selected projects into new top-level folder:
Move Selected Projects into New Folder
var items = document.windows[0].selection.projects
if(items.length > 0){
moveSections(items, new Folder("Project Group"))
}
The DatabaseDocument Class
The DatabaseDocument class is used to create new windows or tabs containing views of the database content.
newWindow() → (Promise) • Returns a Promise that will yield either a newly created and displayed Window or an error. On macOS, this method respects the System Preference governing new window behavior (tab vs. window). That preference is accessible at System Preferences > Dock > Prefer tabs when opening documents.
newTabOnWindow(window:DocumentWindow) → (Promise) • Returns a Promise that will yield either a new tab adjacent to window or an error. This is not available on iOS.
New Window showing Projects Perspective
var winPromise = document.newWindow()
winPromise.then(win => {
win.perspective = Perspective.BuiltIn.Projects
})
winPromise.catch(err => {
console.error(err.message)
})
Add Forecast Tab to Front Window (macOS)
if(app.platformName === 'macOS'){
var winPromise = document.newTabOnWindow(document.windows[0])
winPromise.then(win => {
win.perspective = Perspective.BuiltIn.Forecast
})
winPromise.catch(err => {
console.error(err.message)
})
}
Append Tags of Chosen Task to Selected Tasks
The following Omni Automaton plug-in uses the document selection, the addTags(…) function of the Task class, and interactive Action Forms, to copy the tags of a chosen task to the other tasks selected in the OmniFocus window.
To use, select both the tasks to receive the tags, and the task containing the set of tags to be copied, and run the action. The plug-in offers an option to clear any existing tags from the receiving tasks prior to appending the tags of the source task.
|
Append Tags of Chosen Task to Selected Tasks
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.append-tags-from-selected-task",
"version": "2.1",
"description": "Append tags of the chosen task to the other selected tasks.",
"label": "Append Tags of Chosen Task to Tasks",
"shortLabel": "Copy Tags",
"paletteLabel": "Copy Tags",
"image": "tag"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
tasks = selection.tasks
if (tasks.length > 1){
// sort the task objects by name
tasks.sort((a, b) => {
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
})
} else {
throw new Error("Select two or more tasks.")
}
// generate a list of task names
taskNames = tasks.map(task => {return task.name})
// generate a list of matching indexes
menuIndexes = taskNames.map((item,index) => index)
multiOptionMenu = new Form.Field.MultipleOptions(
"menuKey",
"Select source task",
menuIndexes,
taskNames,
[]
)
checkboxSwitch = new Form.Field.Checkbox(
"clearTags",
"Clear existing tags from selected tasks",
false
)
inputForm = new Form()
inputForm.addField(checkboxSwitch)
inputForm.addField(multiOptionMenu)
inputForm.validate = function(formObject){
var indexes = formObject.values["menuKey"]
// ensure one item is selected
return (indexes.length === 1)?true:false
}
formPrompt = "Append tags of the chosen task to the other selected tasks:"
buttonTitle = "Continue"
formObject = await inputForm.show(formPrompt,buttonTitle)
index = formObject.values["menuKey"]
clearTags = formObject.values["clearTags"]
sourceTask = tasks[index]
sourceTags = sourceTask.tags
tasks.forEach(task => {
if(clearTags){task.clearTags()}
task.addTags(sourceTags)
})
});
action.validate = function(selection, sender){
return (selection.tasks.length > 1)
};
return action;
})();
Displaying (selecting) an Item in the Window
While the focus property of the DocumentWindow class can be used to display specific folders and projects, currently the Omni Automation implementation in OmniFocus does not offer a script function for setting the contents of the Selection class to a specific task. However, you can incorporate the built-in URL support of OmniFocus with scripts to create and execute URLs that will cause specified items to be displayed in the document window.
To create a URL for displaying (selecting) an OmniFocus object, the primaryKey property of the ObjectIdentifer class is appended to the standard OmniFocus URL task protocol, as in the following Omni Automation action that displays the parent project of a selected task:
Display Parent Project of Selected Task
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.display-host-project",
"version": "1.7",
"description": "This action will display the host project of the selected task.",
"label": "Display Host Project",
"shortLabel": "Display Project"
"paletteLabel": "Display Project",
"image": "archivebox"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
project = selection.tasks[0].containingProject
projID = project.id.primaryKey
urlStr = "omnifocus:///task/" + projID
URL.fromString(urlStr).open()
});
action.validate = function(selection, sender){
return (
selection.tasks.length === 1 &&
selection.tasks[0].containingProject
)
};
return action;
})();
(01-10) The Action metadata.
(11-31) The main function containing the action code.
(21-28) The action validation routine will enable the action in the Automation menu when a single task, contained in a project, is selected.
(12-22) The function for creating the action, which is passed the selection object by default.
(15) Use the containingProject property of the Task class to get a reference to the parent project. If the selected task is not included in a project, no value (undefined) will be returned.
(16) The value of the primaryKey property is a unique identifier string.
(17) Append the identifier string to URL string targeting the OmniFocus application.
(18) Use the fromString(…) method of the URL class to convert the string into a URL object, and then execute the url by appending the open() function to the result.
Adding a New Tab Showing Specific Project
How to create and populate a new tab:
New Tab with Project
(async () => {
try {
await document.newTabOnWindow(document.windows[0])
document.windows[1].perspective = Perspective.BuiltIn.Projects
project = flattenedProjects.byName("4-Step Success")
document.windows[1].focus = [project]
}
catch(err){
new Alert(err.name, err.message).show()
}
})();