"Smart" front end for Beyond Compare

Installation

Download the .osp below, then open Preferences / Toolbars / Scripts, and drag it to the list of scripts.

Beyond Compare.osp (14.9 KB)

After that, go to Settings > Customize Toolbars > Commands and there will be a new Compare command which you can drag to your toolbar:

image

The command will do different things depending on what is (or isn't) selected in the file displays, as explained below.

History

The latest version is attached above. Click the relevant links for full details of earlier versions.

  • Original version posted on 21st January, 2014
  • Updated on 22nd January to add support for checked files/folders (check box mode)
  • Version 2.1 (rewrite) posted as an Opus Script Package on 9th April, 2014
  • Version 2.2 (add proper versioning) posted as an Opus Script Package on 10th April, 2014
  • Version 2.2 LD posted on 30th April by Leo Davidson. Includes a number of refinements and improvements. See linked post for full details.
  • Version 2.3 AB posted on 2nd May.
  • Version 2.4 AB posted on 12th June. Minor fix to minimum Opus version check.
  • Version 2.4 AB (at the top of this post), and the 2.2 LD fork (here) have both been updated to include icons for high DPI systems. No other changes. 12th October 2017.
  • Version 2.4 AB (at the top of this post), and the 2.2 LD fork (here) updated again to fix an issue with large icons and high DPI.

I regularly use Beyond Compare to check for differences between versions of files and to synchronise folders. For a long time I have used a toolbar with a variety of buttons to compare two SOURCE files, compare a SOURCE file and a DEST file, compare a SOURCE folder and a DEST folder, etc..

With the introduction of script support in Opus 11 it seemed like a good time to merge all of these discrete buttons into a single "smart" button which examines the environment (e.g. SOURCE only, SOURCE/DEST or DUAL, how many files/folders checked or selected in SOURCE and DEST) and then executes Beyond Compare with the relevant option. For example, if exactly two files are checked or selected in SOURCE, compare these files, or if one folder is checked or selected in SOURCE and one folder in DEST then compare them.

Original button code as follows. (N.B. This has long since been superseded. See above for the latest package.) The only customisation required is to identify where bcompare.exe is on your system. At the time the original code was developed, Beyond Compare v3 was the current version and Beyond Compare 4 was in public Beta so I made provision for switching easily between versions.

Caveat: I used this exercise as an opportunity to learn some JavaScript so my novice code may be a bit clumsy.

// JavaScript button code to provide a smart front end to Beyond Compare
// See http://www.scootersoftware.com for more information regarding Beyond Compare
// Contributed by "AussieBoykie", January 2014
//
// Depending on the presence or absence of DEST and what files/folders are checked or selected in SOURCE and DEST
// the "smart" logic triggers one of the following actions:
//
// Compare the first checked or selected SOURCE file and the second checked or selected SOURCE file
// Compare the first checked or selected SOURCE folder and the second checked or selected SOURCE folder
// or...
// Compare the single checked or selected SOURCE file and the single checked or selected DEST file (if they are different files)
// Compare the single checked or selected SOURCE folder and the single checked or selected DEST folder (if they are different folders)
// or...
// Compare the current SOURCE folder and DEST folder (if they are different folders)
// or...
// Display the Beyond Compare main menu
//
// Before first use, the end user should edit BCCmd (see below) to point to the location of BCompare.exe on the user's system

function OnClick(ClickData)
{
// Define shorthand for the clicked button's command object
var objCmd = ClickData.Func.Command;

// Test the setting of a global variable "debug"
// which is used to control debug output
var debug = objCmd.IsSet("$glob:debug");

// Set boolean flags according to the current SOURCE and DEST check box modes
var SCBMode = objCmd.IsSet("CHECKBOXMODE=On");
if (debug) {DOpus.OutputString("Source CHECKBOXMODE is " + SCBMode)};
var oldSource = objCmd.sourcetab;
if (typeof objCmd.desttab == 'object')
   {
   objCmd.SetSourceTab(objCmd.desttab);
   var DCBMode = objCmd.IsSet("CHECKBOXMODE=On");
   if (debug) {DOpus.OutputString("Dest CHECKBOXMODE is " + DCBMode)};
   objCmd.SetSourceTab(oldSource);
   }
else {var DCBMode = false};

// Test the setting of a global variable "BCBeta" and define the base Beyond Compare command accordingly
// This provides a mechanism for switching between Beyond Compare versions
var BCCmd;
var BCBeta = objCmd.IsSet("$glob:BCBeta");
if (BCBeta)
   {
   BCCmd = "/homeroot\\utilities\\bc4\\bcompare.exe";
   if (debug) {DOpus.OutputString("BCBeta is TRUE")};
   }
else
   {
   BCCmd = "/homeroot\\utilities\\bc3\\bcompare.exe";
   if (debug) {DOpus.OutputString("BCBeta is FALSE")};
   };
var cmd = BCCmd;

// Get some information about which files and/or folders are checked or selected in the SOURCE tab
var sfiles;
var sdirs;
var sitems;
if (!SCBMode)
   {
   sfiles = objCmd.sourcetab.stats.selfiles;
   sdirs = objCmd.sourcetab.stats.seldirs;
   sitems = objCmd.sourcetab.stats.selitems;
   }
else
   {
   sfiles = objCmd.sourcetab.stats.checkedfiles;
   sdirs = objCmd.sourcetab.stats.checkeddirs;
   sitems = objCmd.sourcetab.stats.checkeditems;
   };
if (debug) {DOpus.OutputString("sitems/sfiles/sdirs = " + sitems + "/" + sfiles + "/" + sdirs)};

// Get some information about which files and/or folders are selected in the DEST tab
// Target (DEST) may or may not exist
// Default to zero destination files and folders if no DEST exists
var dfiles = 0;
var ddirs = 0;
var ditems = 0;
if (typeof objCmd.desttab == 'object')
   {
   if (!DCBMode)
      {
      dfiles = objCmd.desttab.stats.selfiles;
      ddirs = objCmd.desttab.stats.seldirs;
      ditems = objCmd.desttab.stats.selitems;
      }
   else
      {
      dfiles = objCmd.desttab.stats.checkedfiles;
      ddirs = objCmd.desttab.stats.checkeddirs;
      ditems = objCmd.desttab.stats.checkeditems;
      };
   };
if (debug) {DOpus.OutputString("ditems/dfiles/ddirs = " + ditems + "/" + dfiles + "/" + ddirs)};

var s1; var s2;
   
// If exactly two files or two folders are checked (in check box mode) or selected in SOURCE
// then prepare a BC command to trigger a Source/Source compare 
if (sitems == 2 && sfiles != sdirs)
   {
   s1 = String(objCmd.sourcetab.selected(0));
   s2 = String(objCmd.sourcetab.selected(1));
   cmd = BCCmd + ' "' + s1 + '"' + ' "' + s2 + '"';
   if (debug) {DOpus.OutputString("Compare Source/Source")};
   }
   
// Otherwise...
// If one file or one folder is selected in both SOURCE and DEST and they are not exactly the same file/folder
// then prepare a BC command to trigger a compare 
else if (sitems == 1 && ditems == 1 && sfiles == dfiles)
   {
   s1 = String(objCmd.sourcetab.selected(0));
   s2 = String(objCmd.desttab.selected(0));
   if (s1 != s2)
      {
      cmd = BCCmd + ' "' + s1 + '"' + ' "' + s2 + '"';
      if (debug) {DOpus.OutputString("Compare Source/Target")};
      }
   }
   
// Otherwise...
// If DEST exists and is not the same as SOURCE then prepare a BC command to trigger a Source/Dest folder compare
else if (typeof objCmd.desttab == 'object' && (objCmd.sourcetab.path != objCmd.desttab.path))
   {
   cmd = BCCmd + ' "' + objCmd.sourcetab.path + '"' + ' "' + objCmd.desttab.path + '"';
   if (debug) {DOpus.OutputString("Compare Source/Target folders")};
   };

// Execute the relevant command
if (debug) {DOpus.OutputString(cmd)};
objCmd.RunCommand(cmd);
}

Regards, AB

4 Likes

Cool, this is something I wanted but hadn't got around to looking at myself.

Thanks this is handy. Reduces my 3 buttons down to 1. Testing on DO 11 Beta 4 x64 ... Not sure why it gets confused by checkbox mode. It doesn't detect selections so it handles checkbox selections as none selected. Looking at the DO 11 Scripting Reference there is no variation of the Selected property for the Tab object for checked files. Still using it though. I've kept two of my original buttons (left-right file compare, left-right folder compare) but converted them both to Three buttons and attached your script to both their right click events. Thanks :slight_smile:

SpiroC

I never thought to look for checked files, probably because I so seldom use check-box mode. It would be easy enough to add code to look for checked items but I'd have to consider how to handle situations where both checked and selected items are present concurrently. Prioritise checked? Prioritise selected? Ignore all selected if check-box mode is active? Consider selected only if nothing is checked? Treat checked and selected as equal? Hmmm.

Regards, AB

Good stuff AB... I may take a swag at some additional checks, though I'm only going off your posted summary and haven't looked closely at the actual code yet, and I use Araxis Merge :wink:. Thanks for sharing!

AB
I'd do the same as DO does. If check-mode active, ignore selected when check mode is active.
SpiroC

SpiroC

After experimenting a bit more I believe I understand the dynamics when Check Box Mode is activated. I will amend the code accordingly (later) and post back when fully tested. Thanks for pointing it out.

Regards, AB

AB
That would be great thanks!! :slight_smile:
SpiroC

In the next beta we'll add a Tab.selstats object which will give statistics that take checkbox mode into account (so e.g. the selfiles property will give the number of checked files in checkbox mode and the number of selected files in non-checkbox mode).

In the meantime is there a way of determining whether the DEST tab is in check box mode?

var objCmd = ClickData.Func.Command;
var CBMode = objCmd.IsSet("CHECKBOXMODE=On");

This tests the active (SOURCE) tab but I have not been able to stumble my way to a valid test for the DEST tab.

Regards, AB

You can make the command object address the destination tab rather than the source tab using the SetSourceTab method, e.g.:

var oldSource = objCmd.sourcetab;
objCmd.SetSourceTab(objCmd.desttab);
// .... do your thing, then restore the tab
objCmd.SetSourceTab(oldSource);

But we hope to have the next beta out today so you may just want to hang on...

I have updated the code to support checked files and folders. See original post at the top of this thread.

Regards, AB

AB
Thanks a lot. Looking good.
SpiroC

Thanks for your work on this AB.I find this handy.

I attempted to tweak the way that the bcompare exe path is stored. I wanted a solution that didn't require the path to be hard coded in the script. I thought we could ask the user for the path and then store it in a persistent variable.

The code to do that could look like this.

var name = "Beyond Compare" ;
var key = "BeyondComparePath";

var result = getResourcePath(name , key);
DOpus.OutputString(key + " value: " + result);

function getResourcePath(name, varKey)
{
  var doFsu = DOpus.FSUtil;
  var doCmd = DOpus.NewCommand;
  var doVars = DOpus.vars;

  var resourcePath;
  if( doVars.Exists(varKey))
  {
    resourcePath = doVars.Get(varKey);
  }

  if(!resourcePath || !doFsu.Exists(doFsu.Resolve(resourcePath)))
  {
    var dlg = DOpus.Dlg;
    var dlgResult = dlg.Open("Locate "+name+" exe", doFsu.Resolve("/homeroot"), DOpus.Listers(0));

    if(dlgResult && dlgResult.result)
    {
      resourcePath = doFsu.Resolve(dlgResult);
      doVars(varKey) = resourcePath;
      doVars(varKey).persist = true;
    }
  }

  if(!resourcePath || !doFsu.Exists(doFsu.Resolve(resourcePath)))
  {
    var dlg = DOpus.Dlg;
    dlg.title = name + " not found";
    dlg.message = "Can't find file\n Path:" + resourcePath ;        
    dlg.buttons = "OK";        
    dlg.icon = "error";
    dlg.Show();
  }
  else
  {
    return resourcePath; 
  }
}

Or as inserted in to your Beyond compare script

@language jscript

// JavaScript button code to provide a smart front end to Beyond Compare
// See http://www.scootersoftware.com for more information regarding Beyond Compare
// Contributed by "AussieBoykie", January 2014
//
// Depending on the presence or absence of DEST and what files/folders are checked or selected in SOURCE and DEST
// the "smart" logic triggers one of the following actions:
//
// Compare the first checked or selected SOURCE file and the second checked or selected SOURCE file
// Compare the first checked or selected SOURCE folder and the second checked or selected SOURCE folder
// or...
// Compare the single checked or selected SOURCE file and the single checked or selected DEST file (if they are different files)
// Compare the single checked or selected SOURCE folder and the single checked or selected DEST folder (if they are different folders)
// or...
// Compare the current SOURCE folder and DEST folder (if they are different folders)
// or...
// Display the Beyond Compare main menu
//
// Before first use, the end user should edit BCCmd (see below) to point to the location of BCompare.exe on the user's system

function OnClick(ClickData)
{
// Define shorthand for the clicked button's command object
var objCmd = ClickData.Func.Command;
var objFsu = DOpus.FSUtil;


// Test the setting of a global variable "debug"
// which is used to control debug output
var debug = objCmd.IsSet("$glob:debug");

// Set boolean flags according to the current SOURCE and DEST check box modes
var SCBMode = objCmd.IsSet("CHECKBOXMODE=On");
if (debug) {DOpus.OutputString("Source CHECKBOXMODE is " + SCBMode)};
var oldSource = objCmd.sourcetab;
if (typeof objCmd.desttab == 'object')
{
objCmd.SetSourceTab(objCmd.desttab);
var DCBMode = objCmd.IsSet("CHECKBOXMODE=On");
if (debug) {DOpus.OutputString("Dest CHECKBOXMODE is " + DCBMode)};
objCmd.SetSourceTab(oldSource);
}
else {var DCBMode = false};

// Test the setting of a global variable "BCBeta" and define the base Beyond Compare command accordingly
// This provides a mechanism for switching between Beyond Compare versions
var BCCmd;
var BCBeta = objCmd.IsSet("$glob:BCBeta");
var name = "Beyond Compare" ;
if (BCBeta)
{
BCCmd = getResourcePath(name , "BeyondCompare3Path");
if (debug) {DOpus.OutputString("BCBeta is TRUE Path is " + BCCmd)};
}
else
{
BCCmd = getResourcePath(name , "BeyondCompare4Path");
if (debug) {DOpus.OutputString("BCBeta is FALSE Path is " + BCCmd)};
};

var cmd = BCCmd;

if(!objFsu.Exists(BCCmd))
{
DOpus.OutputString("cant find bcompare.exe, Path: " + BCCmd);
}

// Get some information about which files and/or folders are checked or selected in the SOURCE tab
var sfiles;
var sdirs;
var sitems;
if (!SCBMode)
{
sfiles = objCmd.sourcetab.stats.selfiles;
sdirs = objCmd.sourcetab.stats.seldirs;
sitems = objCmd.sourcetab.stats.selitems;
}
else
{
sfiles = objCmd.sourcetab.stats.checkedfiles;
sdirs = objCmd.sourcetab.stats.checkeddirs;
sitems = objCmd.sourcetab.stats.checkeditems;
};
if (debug) {DOpus.OutputString("sitems/sfiles/sdirs = " + sitems + "/" + sfiles + "/" + sdirs)};

// Get some information about which files and/or folders are selected in the DEST tab
// Target (DEST) may or may not exist
// Default to zero destination files and folders if no DEST exists
var dfiles = 0;
var ddirs = 0;
var ditems = 0;
if (typeof objCmd.desttab == 'object')
{
if (!DCBMode)
  {
  dfiles = objCmd.desttab.stats.selfiles;
  ddirs = objCmd.desttab.stats.seldirs;
  ditems = objCmd.desttab.stats.selitems;
  }
else
  {
  dfiles = objCmd.desttab.stats.checkedfiles;
  ddirs = objCmd.desttab.stats.checkeddirs;
  ditems = objCmd.desttab.stats.checkeditems;
  };
};
if (debug) {DOpus.OutputString("ditems/dfiles/ddirs = " + ditems + "/" + dfiles + "/" + ddirs)};

var s1; var s2;

// If exactly two files or two folders are checked (in check box mode) or selected in SOURCE
// then prepare a BC command to trigger a Source/Source compare
if (sitems == 2 && sfiles != sdirs)
{
s1 = String(objCmd.sourcetab.selected(0));
s2 = String(objCmd.sourcetab.selected(1));
cmd = BCCmd + ' "' + s1 + '"' + ' "' + s2 + '"';
if (debug) {DOpus.OutputString("Compare Source/Source")};
}

// Otherwise...
// If one file or one folder is selected in both SOURCE and DEST and they are not exactly the same file/folder
// then prepare a BC command to trigger a compare
else if (sitems == 1 && ditems == 1 && sfiles == dfiles)
{
s1 = String(objCmd.sourcetab.selected(0));
s2 = String(objCmd.desttab.selected(0));
if (s1 != s2)
  {
  cmd = BCCmd + ' "' + s1 + '"' + ' "' + s2 + '"';
  if (debug) {DOpus.OutputString("Compare Source/Target")};
  }
}

// Otherwise...
// If DEST exists and is not the same as SOURCE then prepare a BC command to trigger a Source/Dest folder compare
else if (typeof objCmd.desttab == 'object' && (objCmd.sourcetab.path != objCmd.desttab.path))
{
cmd = BCCmd + ' "' + objCmd.sourcetab.path + '"' + ' "' + objCmd.desttab.path + '"';
if (debug) {DOpus.OutputString("Compare Source/Target folders")};
};

// Execute the relevant command
if (debug) {DOpus.OutputString(cmd)};
objCmd.RunCommand(cmd);
}

function getResourcePath(name, varKey)
{
  var doFsu = DOpus.FSUtil;
  var doCmd = DOpus.NewCommand;
  var doVars = DOpus.vars;

  var resourcePath;
  if( doVars.Exists(varKey))
  {
    resourcePath = doVars.Get(varKey);
  }

  if(!resourcePath || !doFsu.Exists(doFsu.Resolve(resourcePath)))
  {
    var dlg = DOpus.Dlg;
    var dlgResult = dlg.Open("Locate "+name+" exe", doFsu.Resolve("/homeroot"), DOpus.Listers(0));

    if(dlgResult && dlgResult.result)
    {
      resourcePath = doFsu.Resolve(dlgResult);
      doVars(varKey) = resourcePath;
      doVars(varKey).persist = true;
    }
  }

  if(!resourcePath || !doFsu.Exists(doFsu.Resolve(resourcePath)))
  {
    var dlg = DOpus.Dlg;
    dlg.title = name + " not found";
    dlg.message = "Can't find file\n Path:" + resourcePath ;        
    dlg.buttons = "OK";        
    dlg.icon = "error";
    dlg.Show();
  }
  else
  {
    return resourcePath; 
  }
}

Thanks again.

For non-portable Beyond Compare installs, the path will be in the registry, so you could use that to find it by default (and only ask if it's not found that way).

e.g. HKEY_CLASSES_ROOT\BeyondCompare.SettingsPackage has the path in the values below it.

Good idea Leo.

Another tweak, beyondcompare does not support some folder paths from dopus, for example libraries.

This can be fixed by using the Filesystem object Resolve method.

//This 
s1 = String(objCmd.sourcetab.selected(0));
s2 = String(objCmd.sourcetab.selected(1));
//Becomes
s1 = objFsu.Resolve(String(objCmd.sourcetab.selected(0)));
s2 = objFsu.Resolve(String(objCmd.sourcetab.selected(1)));

Or as inserted in to your Beyond compare script

@language jscript

// JavaScript button code to provide a smart front end to Beyond Compare
// See http://www.scootersoftware.com for more information regarding Beyond Compare
// Contributed by "AussieBoykie", January 2014
//
// Depending on the presence or absence of DEST and what files/folders are checked or selected in SOURCE and DEST
// the "smart" logic triggers one of the following actions:
//
// Compare the first checked or selected SOURCE file and the second checked or selected SOURCE file
// Compare the first checked or selected SOURCE folder and the second checked or selected SOURCE folder
// or...
// Compare the single checked or selected SOURCE file and the single checked or selected DEST file (if they are different files)
// Compare the single checked or selected SOURCE folder and the single checked or selected DEST folder (if they are different folders)
// or...
// Compare the current SOURCE folder and DEST folder (if they are different folders)
// or...
// Display the Beyond Compare main menu
//
// Before first use, the end user should edit BCCmd (see below) to point to the location of BCompare.exe on the user's system

function OnClick(ClickData)
{
// Define shorthand for the clicked button's command object
var objCmd = ClickData.Func.Command;
var objFsu = DOpus.FSUtil;


// Test the setting of a global variable "debug"
// which is used to control debug output
var debug = objCmd.IsSet("$glob:debug");

// Set boolean flags according to the current SOURCE and DEST check box modes
var SCBMode = objCmd.IsSet("CHECKBOXMODE=On");
if (debug) {DOpus.OutputString("Source CHECKBOXMODE is " + SCBMode)};
var oldSource = objCmd.sourcetab;
if (typeof objCmd.desttab == 'object')
{
objCmd.SetSourceTab(objCmd.desttab);
var DCBMode = objCmd.IsSet("CHECKBOXMODE=On");
if (debug) {DOpus.OutputString("Dest CHECKBOXMODE is " + DCBMode)};
objCmd.SetSourceTab(oldSource);
}
else {var DCBMode = false};

// Test the setting of a global variable "BCBeta" and define the base Beyond Compare command accordingly
// This provides a mechanism for switching between Beyond Compare versions
var BCCmd;
var BCBeta = objCmd.IsSet("$glob:BCBeta");
var name = "Beyond Compare" ;
if (BCBeta)
{
BCCmd = getResourcePath(name , "BeyondCompare3Path");
if (debug) {DOpus.OutputString("BCBeta is TRUE Path is " + BCCmd)};
}
else
{
BCCmd = getResourcePath(name , "BeyondCompare4Path");
if (debug) {DOpus.OutputString("BCBeta is FALSE Path is " + BCCmd)};
};

var cmd = BCCmd;

if(!objFsu.Exists(BCCmd))
{
DOpus.OutputString("cant find bcompare.exe, Path: " + BCCmd);
}

// Get some information about which files and/or folders are checked or selected in the SOURCE tab
var sfiles;
var sdirs;
var sitems;
if (!SCBMode)
{
sfiles = objCmd.sourcetab.stats.selfiles;
sdirs = objCmd.sourcetab.stats.seldirs;
sitems = objCmd.sourcetab.stats.selitems;
}
else
{
sfiles = objCmd.sourcetab.stats.checkedfiles;
sdirs = objCmd.sourcetab.stats.checkeddirs;
sitems = objCmd.sourcetab.stats.checkeditems;
};
if (debug) {DOpus.OutputString("sitems/sfiles/sdirs = " + sitems + "/" + sfiles + "/" + sdirs)};

// Get some information about which files and/or folders are selected in the DEST tab
// Target (DEST) may or may not exist
// Default to zero destination files and folders if no DEST exists
var dfiles = 0;
var ddirs = 0;
var ditems = 0;
if (typeof objCmd.desttab == 'object')
{
if (!DCBMode)
  {
  dfiles = objCmd.desttab.stats.selfiles;
  ddirs = objCmd.desttab.stats.seldirs;
  ditems = objCmd.desttab.stats.selitems;
  }
else
  {
  dfiles = objCmd.desttab.stats.checkedfiles;
  ddirs = objCmd.desttab.stats.checkeddirs;
  ditems = objCmd.desttab.stats.checkeditems;
  };
};
if (debug) {DOpus.OutputString("ditems/dfiles/ddirs = " + ditems + "/" + dfiles + "/" + ddirs)};

var s1; var s2;

// If exactly two files or two folders are checked (in check box mode) or selected in SOURCE
// then prepare a BC command to trigger a Source/Source compare
if (sitems == 2 && sfiles != sdirs)
{
s1 = objFsu.Resolve(String(objCmd.sourcetab.selected(0)));
s2 = objFsu.Resolve(String(objCmd.sourcetab.selected(1)));
cmd = BCCmd + ' "' + s1 + '"' + ' "' + s2 + '"';
if (debug) {DOpus.OutputString("Compare Source/Source")};
}

// Otherwise...
// If one file or one folder is selected in both SOURCE and DEST and they are not exactly the same file/folder
// then prepare a BC command to trigger a compare
else if (sitems == 1 && ditems == 1 && sfiles == dfiles)
{
s1 = objFsu.Resolve(String(objCmd.sourcetab.selected(0)));
s2 = objFsu.Resolve(String(objCmd.desttab.selected(0)));
if (s1 != s2)
  {
  cmd = BCCmd + ' "' + s1 + '"' + ' "' + s2 + '"';
  if (debug) {DOpus.OutputString("Compare Source/Target")};
  }
}

// Otherwise...
// If DEST exists and is not the same as SOURCE then prepare a BC command to trigger a Source/Dest folder compare
else if (typeof objCmd.desttab == 'object' && (objCmd.sourcetab.path != objCmd.desttab.path))
{
cmd = BCCmd + ' "' + objFsu.Resolve(objCmd.sourcetab.path) + '"' + ' "' + objFsu.Resolve(objCmd.desttab.path) + '"';
if (debug) {DOpus.OutputString("Compare Source/Target folders")};
};

// Execute the relevant command
if (debug) {DOpus.OutputString(cmd)};
objCmd.RunCommand(cmd);
}

function getResourcePath(name, varKey)
{
  var doFsu = DOpus.FSUtil;
  var doCmd = DOpus.NewCommand;
  var doVars = DOpus.vars;

  var resourcePath;
  if( doVars.Exists(varKey))
  {
    resourcePath = doVars.Get(varKey);
  }

  if(!resourcePath || !doFsu.Exists(doFsu.Resolve(resourcePath)))
  {
    var dlg = DOpus.Dlg;
    var dlgResult = dlg.Open("Locate "+name+" exe", doFsu.Resolve("/homeroot"), DOpus.Listers(0));

    if(dlgResult && dlgResult.result)
    {
      resourcePath = doFsu.Resolve(dlgResult);
      doVars(varKey) = resourcePath;
      doVars(varKey).persist = true;
    }
  }

  if(!resourcePath || !doFsu.Exists(doFsu.Resolve(resourcePath)))
  {
    var dlg = DOpus.Dlg;
    dlg.title = name + " not found";
    dlg.message = "Can't find file\n Path:" + resourcePath ;
    dlg.buttons = "OK";
    dlg.icon = "error";
    dlg.Show();
  }
  else
  {
    return resourcePath;
  }
}

Since my first hack at this I discovered that long paths could cause a problem. The final BC command string is of the form..

<some path>\bcompare.exe <spec1> <spec2>

The combined length of <spec1> and <spec2> can cause problems if one or other or both is very long. In an attempt to avoid problems my revised code now changes directory before executing the BC command. The target directory is either <spec1> or <spec2>, whichever is longest which means that the target filespec can be specified without its path - e.g. some.txt instead of C:\SomeUnreasonablyLongAndComplexPath\some.txt.

I rewrote in VBScript because it is a more familiar environment. @wowbagger I note your comments about using the Filesystem object Resolve method and will look to incorporate once I fully get to grips with how this works. In the meantime here is my revised VBS code. The logic of what is compared under what circumstances is embedded in comments in the code. It suits me the way it is but is relatively easy to tweak if required.

@language vbscript

option explicit

' "Smart" front end to Beyond Compare
' (Re)Written by AussieBoykie in VBScript because it is more familiar to the author! 8-)
' Handles very long paths as best possible
' Logic for what is actually compared is articulated in comments in the code. See below..

Function OnClick(ByRef ClickData)
   
   Dim cdf
   Dim sPath
   Dim dPath
   Dim nodest
   Dim src
   Dim tgt
   Dim l
   Dim r
   Dim n
   Dim pair
   Dim cd
   Dim BCBeta
   Dim BCVer
   Dim BCCmd
   Dim BCArgs
   Dim cmdstring
   
   Const vbQuote = """"
   Const dbg = TRUE ' Switch to FALSE to disable or TRUE to enable debug output
   
   Set cdf = ClickData.func

   ' Test the global variable BCBeta to decide whether to use Beyond Compare 3 (stable) or 4 (beta)
   ' Edit the BCCmd = statement below to reflect the correct path to the BC executable on your system   
   BCBeta = cdf.Command.IsSet("$glob:BCBeta")
   if BCBeta then BCVer = 4 else BCVer = 3
   BCCmd = "/homeroot\utilities\bc" & BCVer & "\bcompare.exe"
   if dbg then DOpus.Output "BCCmd: " & BCCmd
      
   Set src = cdf.sourcetab
   sPath = src.path
   nodest = cdf.desttab = 0
   if nodest then
      Set tgt = src
      dPath = sPath
   else
      set tgt = cdf.desttab
      dPath = tgt.path
   end if
   if len(dPath) > len(sPath) then
      cd = dPath
   else
      cd = sPath
   end if
   
   if dbg then DOpus.Output "Sourcetab: " & sPath
   if dbg then DOpus.Output "Desttab: " & dPath

   ' The final BC command string is of the form <some path>\bcompare.exe <spec1> <spec2>
   ' The combined length of <spec1> and <spec2> can cause problems if one or other or both is very long
   ' In an attempt to avoid problems we will change directory before executing the BC command
   ' The target directory will be either <spec1> or <spec2>, whichever is longest
   ' The target name can then be specified without its path - e.g. some.txt instead of C:\SomeLongPath\some.txt
   if right(sPath,1)="\" then
      n=1
   else
      n=2
   end if
   pair = false
   if src.selected_files.count = 2 then
      l = mid(src.selected_files(0),len(sPath)+n)
      r = mid(src.selected_files(1),len(sPath)+n)
      pair = true
   elseif src.selected_dirs.count = 2 then
      l = mid(src.selected_dirs(0),len(sPath)+n)
      r = mid(src.selected_dirs(1),len(sPath)+n)
      pair = true
   end if

   ' If SOURCE and DEST are the same then...
   ' If exactly two files are selected (whether or not folders are also selected)
   ' or exactly two folders are selected (whether or not files are also selected)
   ' then execute a compare, otherwise just bring up the Beyond Compare main menu
   if sPath = dPath then ' Source only
      cd = sPath
      if pair then
         BCArgs = vbQuote & l & vbQuote & " " & vbQuote & r & vbQuote
      else
         BCArgs = ""
      end if
   ' If SOURCE and DEST are different then...
   ' If exactly one SOURCE file and one DEST file are selected (whether or not folders are also selected)
   ' or exactly one SOURCE folder and one DEST folder are selected (whether or not files are also selected)
   ' then execute a compare, otherwise...
   ' if a pair of files or a pair of folders is selected in SOURCE then execute a compare, otherwise
   ' compare SOURCE and DEST folders
   else
      if src.selected_files.count = 1 and tgt.selected_files.count = 1 then
         if cd = sPath then
            l = mid(src.selected_files(0),len(sPath)+n)
            r = tgt.selected_files(0)
	 else
            l = src.selected_files(0)
            r = mid(tgt.selected_files(0),len(dPath)+n)
	 end if
	 BCArgs = vbQuote & l & vbQuote & " " & vbQuote & r & vbQuote
      elseif src.selected_dirs.count = 1 and tgt.selected_dirs.count = 1 then
         if cd = sPath then
            l = mid(src.selected_dirs(0),len(sPath)+n)
            r = tgt.selected_dirs(0)
	 else
            l = src.selected_dirs(0)
            r = mid(tgt.selected_dirs(0),len(dPath)+n)
	 end if
         BCArgs = vbQuote & l & vbQuote & " " & vbQuote & r & vbQuote
      elseif pair then
         BCArgs = vbQuote & l & vbQuote & " " & vbQuote & r & vbQuote
      else
         BCArgs = vbQuote & sPath & vbQuote & " " & vbQuote & dPath & vbQuote
      end if
   end if

   cmdstring = vbQuote & BCCmd & vbQuote & " " & BCArgs
   if dbg then DOpus.Output cmdstring
   With cdf.Command
      .deselect = FALSE
      .addline "cd " & spath
      .addline cmdstring
      .run
   End With

End Function

Regards, AB

Interesting bug, if we can work out where is the braking length we could only change the target folder if the paths are too long. Not sure how using filesystem resolve will work considering the path length bug.
Did you see my other post re the path to the exe? I used the same code for thumbnail script I posted. I can re write the code in VB.

regards

Yes I did. I use the portable version of BC so it lives wherever I put it and I have to hard code the path. By all means post an EXE path version for use by those who prefer to install.

Regards, AB

I think I didn't explain correctly. I also have BC installed as portable. Further to this, I have it installed in different locations on different PC's.
Ideally I would like scripts that required external exe's to ask me for the path the first time it runs, then keep using that path unless the exe goes missing (is moved).
This means when I move the scripts between PCs, or when I download updates to scripts,I don't need to edit the script to set the path I need. It makes the scripts more more portable/shareable.

That aside, How do you set the debug flag? Curious what your work flow is here?
I have been just editing the script debug = true, I guess you could have a button that sets it?

thanks