Saving Omnifocus window layout and restoring it

Update: Did cobble together simple JXA scripts, in a message below here. Link to that message.


Objective:

  1. To save a list of open windows and what is open in the window (perspective, project, tag etc). The window dimensions. And window options - whether sidebar, toolbar and information bar is open or not.
  2. To restore Omnifocus windows from the list saved by the previous step.

I believe this will need two separate scripts.
The list can be saved in a file in some format that a program can easily read.

Is this something anybody else has done already? Or even if you have seen a partial script that can be built upon, do please share.

I am sure somebody has done this before.

Thank you!


I had an earlier script, may be from the forum itself, but looking at the output I am not able to see any field that describes whether sidebar, information bar and tool bar are hidden or visible. How do I capture that information? Thank you!

tell application "OmniFocus"
	set myWins to every window
	set realWins to {}
	set myWinNames to {}
	repeat with myWin in myWins
		if visible of myWin is true then
			return properties of myWin
			set winName to name of myWin
			copy winName to end of myWinNames
			copy myWin to end of realWins
		end if
	end repeat
end tell

So I have decided to go with JXA to try and do this. Different matter that I do not know javascript yet :(, but will cobble something together.

I have some questions in the comments in the script below - will appreciate any help that somebody who knows can provide as to how to capture the information.

function run() {
    const app = Application("OmniFocus");
    app.includeStandardAdditions = true;

    const windows = app.defaultDocument.documentWindows()

    for (window of windows){
        // get details for each window to be able to open it again - perspective, or tag, or project, sidebar - y/n, infobar - y/n, toolbar - y/n, window size / bounds - x,y and size of x,y
        var dict = {};
        dict["perspective"] = window.perspectiveName();
        // if perspectiveName is "Projects" then how to figure what the sidebar selection is, especially if sidebar is hidden? Could not find this in the properties of the window.

        // dict["sidebar_visible"] = how_to_capture_whether_sidebar_hidden_or_not() // may need to use system events to figure out if sidebar is showing for the window or not based on whether the hide sidebar is visible or show sidebar
        // dict["toolbar_visible"] = need_to_capture_this()
        console.log(JSON.stringify(window.properties()));
        // Now to push this dictionary into an array
   }

    // Now save this array into a json file to be loaded by the script that is to recreate the OmniFocus window layout

}

And then I will try to write a script that reads the son file from previous script, and launches the window one by one. Still not clear as to how to manage sidebar visibility etc, but may have to use System Events for that.

Thank you!

I simply exit with and it saves all the perspective tabs, not sure about inspector panes etc.

That is fair. And that does work.

It does not work if for any reason OmniFocus did not close normally or if the screen size changes - external monitor to laptop.

So I tried the below included scripts. They are clunky. But sort of work.

// this script prints in the console what the current windows are and their coordinates.
// this information as of now needs to be copied to the next script manually - copy paste etc.

function run() {
    const omniFocus = Application("OmniFocus");
    omniFocus.includeStandardAdditions = true;

    const windows = omniFocus.defaultDocument.documentWindows();

    for (window of windows) {
        console.log(JSON.stringify(window.properties()));
        if (window["visible"] === true){
        console.log(JSON.stringify(window.properties()));
        }
    }
}

This script closes all existing windows, and then launches new windows per what is declared at the top in the script.

function run() {
    const omniFocus = Application("OmniFocus");
    omniFocus.includeStandardAdditions = true;

    const curr_windows = omniFocus.defaultDocument.documentWindows();

    // close all existing windows since new windows will be launched
    for (window of curr_windows) {
        console.log(window.name());
        window.close();
        delay(0.5);
    }

    target_windows_on_laptop = [
        {"type":"window", "bounds":{"x":5,"y":24,"width":637,"height":530},"perspectiveName":"Forecast"},
        {"type":"tab", "bounds":{"x":12,"y":25,"width":1036,"height":530},"perspectiveName":"daily"},
        {"type":"tab", "bounds":{"x":9,"y":25,"width":636,"height":530},"perspectiveName":"Inbox"},
    ];

    target_windows_on_external_monitor = [
        {"type":"window","perspectiveName":"daily", "bounds":{"x":3,"y":909,"width":643,"height":521}, "Sidebar":"N", "Toolbar": "N", "Inspector":"N"},
        {"type":"window","bounds":{"x":5,"y":523,"width":641,"height":385},"perspectiveName":"Inbox", "Sidebar":"N", "Toolbar": "N", "Inspector":"N"},
        {"type":"window","bounds":{"x":650,"y":1041,"width":632,"height":389},"perspectiveName":"Calls", "Sidebar":"N", "Toolbar": "N", "Inspector":"N"},
   ]

    // decide the target based on which screen
    target_windows = target_windows_on_external_monitor;

    for (window of target_windows){
        var of_window = omniFocus.DocumentWindow({ perspectiveName: window["perspectiveName"], "bounds":window["bounds"]});
        omniFocus.defaultDocument.documentWindows.push(of_window); // this will create the window
        omniFocus.activate();
        // now time to check if Sidebar, Toolbar and Inspector are the way they should be
        // set_bar("Sidebar",window["Sidebar"]);
        for (whichBar of ["Sidebar", "Toolbar", "Inspector"]) {
            if (window[whichBar] === "N"){
                menuItemString = "Hide " + whichBar;
            } else {
                menuItemString = "Show " + whichBar;
    
            }
            result = menuItemTestClick("OmniFocus", ['View', menuItemString]);
            // console.log(window["perspectiveName"] + " Result of click for " + menuItemString + " is " + result);
        }

        var win = omniFocus.defaultDocument.documentWindows[0];
        // return win.properties();
        // console.log("Name of window is " + win.name);
        win.bounds = window["bounds"];

        // return;
    }

}


// source: https://gist.github.com/bumaociyuan/a37c78cd5503f371b391
// TESTING VERSION: REPORTS ANY ERRORS IN DETAIL OF MENU PATHS

// Click an OS X app sub-menu item
// 2nd argument is an array of arbitrary length (exact menu item labels, giving full path)
//menuItemClick("InqScribe", ['View', 'Aspect Ratio', 'Use Media Ratio'])
function menuItemTestClick(strAppName, lstMenuPath) {
	var oApp = Application(strAppName),
		lngChain = lstMenuPath.length,
		blnResult = false

	var blnDebug = false; // edit to 'true' if you want warnings of errors in the path
	// (NOTE this kind of checking slows the script, so edit to blnDebug=false for regular use)

	if (lngChain > 1) {

		var appSE = Application("System Events"),
			lstApps = appSE.processes.where({
				name: strAppName
			}),
			procApp = lstApps.length ? lstApps[0] : null;

		if (procApp) {
			var strMenu = lstMenuPath[0],
				strPath = strMenu,
				fnMenu = procApp.menuBars[0].menus.byName(strMenu),
				lngLast = lngChain - 1,
				mnuItem;

			if (blnDebug) {
				try {
					fnMenu.class()
				} catch (e) {
					throw "Menu Name " + '"' + strMenu + '"' + " not found in " + strAppName
				}
			}

			for (var i = 1; i < lngLast; i++) {
				strMenu = lstMenuPath[i];
				strPath = strPath + ' > ' + strMenu;
				fnMenu = fnMenu.menuItems[strMenu].menus[strMenu];
				if (blnDebug) {
					try {
						fnMenu.class()
					} catch (e) {
						throw "Menu item " + '"' + strPath + '"' + " not found in " + strAppName
					}
				}
			}

			strMenu = lstMenuPath[lngLast];
			strPath = strPath + ' > ' + strMenu;
			mnuItem = fnMenu.menuItems[strMenu];

			if (blnDebug) {
				try {
					mnuItem.class()
				} catch (e) {
					throw "Menu item " + '"' + strPath + '"' + " not found in " + strAppName
				}
			}

			oApp.activate()
			try {
                mnuItem.click();
                delay(1);
			    blnResult = true;
            } catch (e) {
                if (blnDebug){
                    console.log("Menu item" + '"' + strPath + '"' + " not found in " + strAppName);
                }
            }
		}
	}
	return blnResult;
}