Dynamic SWITCHPATH v2.2

Overview

See the old post for v1.0 for an overview of what this does:

One new feature from v1.0 is the jump list which displays the entire config file in a menu, acting as a secondary favorite list and a gateway to getting to a switchpath group. The width of the jump menu can also be adjusted.

Usage

So far all possible commands in DOpus are:

  • DynSwitchPath or DynSwitchPath FORWARD will cycle forward
  • DynSwitchPath BACK will cycle backward
  • DynSwitchPath MENU=opt or DynSwitchPath MENU=options
  • DynSwitchPath MENU=jump or DynSwitchPath MENU=jumplist

Download

Installation (script)

After unzipping the archive, you should have these files:

  • dynamic_switchpath.js
  • dynamic_switchpath.txt

Type /dopusdata\Script AddIns into Opus to go to the scripts folder, and move both files there.

Installation (buttons)

NOTE: Right clicking the buttons is how to access the menus

For instructions on how to paste these code blocks on to a toolbar, see here:

Forward Button

<?xml version="1.0"?>
<button backcol="none" display="both" textcol="none" type="three_button">
	<label>DSP_forward</label>
	<icon1>#default:goforward</icon1>
	<button backcol="none" display="both" textcol="none">
		<label>DSP Forward</label>
		<icon1>#default:goforward</icon1>
		<function type="normal">
			<instruction>DynSwitchPath FORWARD</instruction>
		</function>
	</button>
	<button backcol="none" display="both" textcol="none">
		<label>DSP Jump Menu</label>
		<icon1>#default:goforward</icon1>
		<function type="normal">
			<instruction>DynSwitchPath MENU=jump</instruction>
		</function>
	</button>
</button>

Back Button

<?xml version="1.0"?>
<button backcol="none" display="both" textcol="none" type="three_button">
	<label>DSP_back</label>
	<icon1>#default:goback</icon1>
	<button backcol="none" display="both" textcol="none">
		<label>DSP Back</label>
		<icon1>#default:goback</icon1>
		<function type="normal">
			<instruction>DynSwitchPath BACK</instruction>
		</function>
	</button>
	<button backcol="none" display="both" textcol="none">
		<label>DSP Options</label>
		<icon1>#default:goback</icon1>
		<function type="normal">
			<instruction>DynSwitchPath MENU=opt</instruction>
		</function>
	</button>
</button>

Example configuration file

------dynamic_switchpath.txt-------
C:\
D:\<--------------------- Group 1
E:\
-
C:\Program Files
C:\Windows<----------- Group 2
C:\Windows\system32

Known Caveats

  • Requires DO11 beta6 or greater.
  • ConfFile: Paths must not end with a backslash (\) (with one exception, see below).
  • ConfFile: Cycling through drives root like C:\ and D:\ they must end with a backslash (\).
  • ConfFile: Try to avoid duplicate path entries throughout the whole file, because the first one found will always run, never the second.

Changelog:

v2.2 (03-04-2015)

  • Fixed case sensitive checking on folders (described below)
  • Fixed error when cancelling out of jump menu width dialog
  • Check for minimum dopus version

v2.1 (02-09-2014)

  • Added: Expand to Tabs (currently only works if path of focused tab is one that exists in config file)
  • Added: Where to expand Source/Destination/Ask
  • Added: Options Menu
  • Added: Jump Width Dialog
  • Creates two global vars, dspOptions and dspJumpWidth

v2.0 (02-02-2014)

  • Converted from batch script to jscript
  • Added: Jump List Menu

v1.0 (05-30-2012)

Script Code:

The script code from the v2.2 download above is reproduced here for reference.

Note that the download also includes a config file which you still need. The script is primarily reproduced here to help people looking for ways to do things.

// 2014 yonder

// Note if the comments aren't aligning try changing tabstop to 4 in external editor
// Turn line wrap off for ideal viewing

// file scope
var debug = false;
var cmd;
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
var jumpRun = DOpus.NewVector();									// full length menu names
var jump1 = DOpus.NewVector();										// truncated menu names
var jump2 = DOpus.NewVector();										// menu item flags (bold, checked)
var startTime = new Date().getTime();								// calc runtime in ms
var fShowJump = false;												// jump or jumplist set ?
var dspOptions = 0;													// glob! var
var dspJumpWidth = 40;												// glob! var
//var soundFile = "C:\\WINDOWS\\Media\\Windows XP Ding.wav";

function OnInit(initData)
{
	initData.name = "Dynamic SWITCHPATH";
	initData.desc = "Multiple switchpath definitions to cycle through, from one command. " +
					"Based off current viewing folder in focus. " +
					"(DOpus 11.0 Beta 6 or higher required)";
	initData.copyright = "Feb-2014 yonder";
	initData.version = "2.2";
	initData.default_enable = true;
	initData.min_version = "11.4";

	// define and initialise command
	var cmd = initData.AddCommand();
	cmd.name = "DynSwitchPath";
	cmd.method = "dspMain";
	cmd.desc = initData.desc;
	cmd.label = "DynSwitchPath";
	cmd.template = "FORWARD/S/O,BACK/S/O,MENU/K[opt,jump]/O";

	return false;
}

//////////
// Main
//////////
function dspMain(funcData)
{
	var cntAll = 0;													// counter will only count everything when jump menu is issued
	var cntGrpPaths = 0;											// counter; how many paths per group
	var cntGrps = 0;												// counter; how many groups
	var source = 0;													// choice from "ask" sub menu
	var primary = "", allOther = "";								// Go "primary" SWITCHPATH "all" "other" "paths"
	var group = new Array();
	
	var back = false;
	var match = false;
	
	cmd = funcData.func.Command;
	cmd.Clear();
	cmd.ClearFiles();

	// dspOptions glob!
	if (DOpus.Vars("dspOptions").Exists())
		dspOptions = DOpus.Vars("dspOptions").value;
	else
	{
		// set defaults
		dspOptions |= 8;
		
		DOpus.Vars("dspOptions") = dspOptions;
		DOpus.Vars("dspOptions").persist = true;
	}

	// dspJumpWidth glob!
	if (DOpus.Vars("dspJumpWidth").Exists())
		dspJumpWidth = DOpus.Vars("dspJumpWidth").value;
	else
	{
		DOpus.Vars("dspJumpWidth") = dspJumpWidth;
		DOpus.Vars("dspJumpWidth").persist = true;
	}

	if (dspOptions & 1)
	{
		debug = true;
		if (dspOptions & 2)
			DOpus.ClearOutput();
	}
	
	// parse parameters
	if (funcData.func.args.got_arg.back) back = true;
	if (funcData.func.args.got_arg.menu)
	{
		switch (funcData.func.args.menu)
		{
		case "opt":
		case "options":
			var r = showOptMenu(funcData.func.Dlg);
			if (r == 0)
				return false;
			else
				source = r;
			break;
		case "jump":
		case "jumplist":
			fShowJump = true;
		}
	}

	// pwd (.\\) was returning /home, so using alias to get current working dir
	var dopusData = DOpus.Aliases("dopusdata").path;
	var cfgFile = dopusData + "\\Script AddIns\\dynamic_switchpath.txt";
	if (!objFSO.FileExists(cfgFile))
	{
		d("Config file not found: \"/dopusdata\\Script AddIns\\dynamic_switchpath.txt\"", true);
		cmd.RunCommand("Set UTILITY=otherlog");
		//soundOnError();
		return false;												// fail
	}
	
	// process config file and execute command or show jump menu
	var srcPath = funcData.func.sourcetab.path;
	var objFile = objFSO.OpenTextFile(cfgFile, 1, false);

	while (!objFile.AtEndOfStream)
	{
		var line = trim(objFile.ReadLine());

		if (line.substr(0, 2) == "//") continue;					// ignore comments
		if (line == "-")
		{
			cntGrps++;
			if (match && !fShowJump) break;
			cntGrpPaths = -1;
			primary = "";
			allOther = "";
			group = [];
		}

		if (cntGrpPaths == 0)
			group[cntGrpPaths] = primary = "\"" + line + "\"";
		else if (cntGrpPaths > 0)
		{
			if (back)
				allOther = "\"" + line + "\" " + allOther;
			else
				allOther = allOther + " \"" + line + "\"";
			group[cntGrpPaths] = "\"" + line + "\"";
		}

		if (line.toLowerCase() == lcase(srcPath)) match = 1;
		if (fShowJump)
		{	// prep jump menu
			jump1.push_back(truncatePath(line, dspJumpWidth));
			jumpRun.push_back(line);

			if (line.toLowerCase() == lcase(srcPath))
				jump2.push_back(1);
			else
				jump2.push_back(0);
		}

		cntGrpPaths++; cntAll++;
	}

	objFile.Close();
	
	primary = trim(primary);
	allOther = trim(allOther);
	
	if (match && source)
	{	// expand to tabs
		var openInDest = "";
		for (var i=0; i < group.length; i++)
		{
			if(group[i] == ("\"" + srcPath + "\"")) continue;
			if(source == 2) openInDest = "OPENINDEST";
			cmd.AddLine("Go " + group[i] + " NEWTAB " + openInDest);
		}
		cmd.Run();
	}
	else if (match && !fShowJump)
	{	// switchpath
		cmd.RunCommand("Go " + primary + " SWITCHPATH " + allOther);
		d(cntGrps + " " + cntGrpPaths + " " + primary + " " + allOther);
		
		var elapsedTime = new Date().getTime() - startTime;
		d(elapsedTime + " milliseconds");
		delete elapsedTime;
	}
	else if (fShowJump)
	{	// jump list
		var dlg = funcData.func.Dlg;
		dlg.choices = jump1;
		dlg.menu = jump2;
		var r = dlg.Show();

		if (r > 0)
		{
			cmd.RunCommand("Go " + "\"" + jumpRun(r - 1) + "\"");
			d(jumpRun(r - 1));
		}
	}
	delete group, startTime, objFSO;
	return true;
}

/////////////////
// prepOptMenu
/////////////////
function showOptMenu(dlg)
{
	var tmpJumpWidth = dspJumpWidth;
	if ((parseInt(tmpJumpWidth)) == 255)
		tmpJumpWidth = "MAX";

	var opt1 = DOpus.NewVector(/*"Tabs to SwitchPath, Save",*/
							   /*"Tabs to SwitchPath, Temp",*/
							   "Expand to Tabs", "-",
							    "Ask", "Open in Source", "Open in Destination", "-",
							   /*"Update Toolbar Icon(s)",*/
							   "Jump Menu Width: " + tmpJumpWidth, "-",
							   "Debug", "Clear Output on Run");
	var opt2 = DOpus.NewVector(opt1.length);

	// load options from glob! var
	for (var i = 0; i < opt1.length; i++)
	{
		switch (opt1(i))
		{
		case "Ask":
			if (dspOptions & 8)
			{
				opt2(i) = 6;
				opt2(i+1) = 0;
				opt2(i+2) = 0;
			}
			break;
		case "Open in Source":
			if (!(dspOptions & 4) && !(dspOptions & 8))
				opt2(i) = 6;
			break;
		case "Open in Destination":
			if ((dspOptions & 4) && !(dspOptions & 8))
				opt2(i) = 6;
			break;
		case "Debug":
			if (dspOptions & 1)
			{
				opt2(i) = 2;
				opt2(i+1) &= ~8;
			}
			else
				opt2(i+1) |= 8;
			break;
		case "Clear Output on Run":
			if (dspOptions & 2)
				opt2(i) |= 2;
			break;
		}
	}

	// show menu
	dlg.choices = opt1;
	dlg.menu = opt2;
	var r = dlg.Show();

	// clicked item
	if (r > 0)
	{
		switch (opt1(r - 1))
		{
		case "Expand to Tabs":
			if (dspOptions & 8)
				return showSubMenu("ask_src_dest");
			else if (!(dspOptions & 4) && !(dspOptions & 8))
				return 1; // src
			else if ((dspOptions & 4) && !(dspOptions & 8))
				return 2; // dest
			break;
		case "Ask": dspOptions |= 8; break;
		case "Open in Source": dspOptions &= ~4 & ~8; break;
		case "Open in Destination": dspOptions |= 4; dspOptions &= ~8; break;
		case "Jump Menu Width: "+tmpJumpWidth: showJumpWidthDlg(); break;
		case "Debug": dspOptions ^= 1; if (dspOptions & 1) debug = true; break;
		case "Clear Output on Run": dspOptions ^= 2; break;
		}
		DOpus.Vars("dspOptions") = dspOptions;
		d("dspOptions: " + pad(dspOptions, 3) + " (" + pad(d2b(dspOptions), 8) + ")");
		//return -1;
	}
	return false;
}

/////////////////////
// showJumpWidthDlg
/////////////////////
function showJumpWidthDlg()
{
	var dlg = DOpus.Dlg;
	var input, r;

	dlg.window = DOpus.Listers(0);			// 0 seems to be the lister in focus (z-order?)
	dlg.title = "Jump menu width";
	dlg.message = "Specify desired jump menu width.\n\nAvailable range: 0-255";
	dlg.buttons = "OK|Cancel";
	dlg.Default = dspJumpWidth.toString();
	dlg.max = 3;

	while (true) // only accept numeric input
	{
		r = dlg.Show();
		if (!r) break;
		
		input = trim(dlg.input);
		if (input.match(/^\d+$/) != null)
		{
			if ((parseInt(input)) > 255)
				input = "255";
			DOpus.Vars("dspJumpWidth") = dspJumpWidth = input;
			break;
		}

		dlg.message = "Specify desired jump menu width.      >> NUMBERS ONLY <<\n\nAvailable range: 0-255";
		dlg.Default = input;
	}
	d("dlgJumpWidth: (" + r + ") " + input);
}

////////////////
// showSubMenu
////////////////
function showSubMenu(menu)
{
	var sub1 = DOpus.NewVector();
	var sub2 = DOpus.NewVector();
	var r;

	var dlg = DOpus.Dlg;
	dlg.window = DOpus.Listers(0);
	dlg.choices = sub1;
	dlg.menu = sub2;

	switch (menu)
	{
	case "ask_src_dest": 					// return 1 for src, 2 for dest, 0 fail
		var src = "Source";
		var dest = "Destination";
		
		var dual = DOpus.Listers(0).dual;
		if (cmd.sourcetab.right)
		{
			switch (dual)
			{	
			case 1:
				src = "(Right)  " + src;
				dest = "(Left)  " + dest; break;
			case 2:
				src = "(Bottom)  " + src;
				dest = "(Top)  " + dest; break;
			}
		}
		else
		{
			switch (dual)
			{		
			case 1:
				src = "(Left)  " + src;
				dest = "(Right)  " + dest; break;
			case 2:
				src = "(Top)  " + src;
				dest = "(Bottom)  " + dest; break;
			}
		}
		
		sub1.push_back(src);
		sub2.push_back(0);
		sub1.push_back(dest);
		sub2.push_back(0);
		return dlg.Show();
	}
	return 0;
}

//////////////////
// truncatePath
//////////////////
function truncatePath(path, width)
{	// truncate to length of width
	if (path == "-") return path;
	if (path.length < width) return path;

	var slash_pos = path.lastIndexOf("\\");
	var last_folder = path.substr(slash_pos, path.length - slash_pos);
	var remainder = width - last_folder.length - 3;  // -3 for ...
	return path.substr(0, remainder) + "..." + last_folder;
}

//function soundOnError() {cmd.RunCommand("Play \"" + soundFile + "\" QUIET");}
function d2b(d) {return d.toString(2);} 	// decimal to binary
function pad(n, z) {return Array(Math.max(z - String(n).length + 1, 0)).join(0) + n;}
function lcase(s) {return String.prototype.toLowerCase.apply(s);}
function trim(s) {return s.replace(/^\s+|\s+$/g,"");}
function d(o) {d(o, false);}
function d(o, override) {if(debug || override) DOpus.Output(o);}

/* dspOptions bit flags

0x01 (01)	debug
0x02 (02)	clearOutput
0x04 (04)	0=Src, 1=Dest
0x08 (08)	Ask
0x10 (16)	jumpListIndicator

*/

I will be replying on every script update. Since editing the original post does not reflect changes at the forum home page (timestamp, etc), replying does. :slight_smile:

Updated to 2.1

I just found out the script (v2.1) has trouble runnin' on windows 7 (at least for me).
The fix until the next script update is to delete all 4 occurances of...

.toLowerCase()

example:
if (line.toLowerCase() == srcPath.toLowerCase())
to
if (line == srcPath)

Then the upper/lowercase would all have to be correct in the config file to match your actual folders.

Hi,

Not sure if i'm being thick, but i can't see a explanation of what exactly this script does. As a layman looking at this I have no idea really what it does.

Can you add a description or similar please.

thanks

The link in the overview section of the first post leads to a description.

Updated to 2.2

Sorry for delay...

@yonder Do you still have the latest v2.2 zip file? For some reason it is not on the forum server, nor in any backups.

I've reuploaded, thanks.

Many thanks!

Thanks for the nice script