GP SoftwareTwitter
Opus FAQsManualCommandsObjects

"Smart" front end for Beyond Compare


#1

Installation

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

Beyond Compare.osp (15.0 KB)

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.

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


External Compare and Merge Tools 2.0
Help passing left and right selected folders in simple button
Synchronise is SO slow
Global Variable Management Dialog
Beyond Compare buttons
Beyond Compare buttons
How to get scrolling in both panels of comparison results simultaneously
Using Opus to identify differences in similar directory trees
Button to pass active path from both listers to BeyondCompare 4.0 for folder comparison
Passing files to external program fails when in different listers
#2

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


#3

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:


#4

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


#5

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!


#6

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


#7

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


#8

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


#9

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).


#10

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


#11

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...


#12

[quote="SpiroC"]AB
That would be great thanks!! :slight_smile:
SpiroC[/quote]
I have updated the code to support checked files and folders. See original post at the top of this thread.

Regards, AB


#13

AB
Thanks a lot. Looking good.
SpiroC


#14

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.


#15

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.


#16

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;
  }
}

#17

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

\bcompare.exe

The combined length of and 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 or , 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.

[code]@language vbscript

option explicit

' "Smart" front end to Beyond Compare
' (Re)Written by AussieBoykie in VBScript because it is more familiar to the author! :sunglasses:
' 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 \bcompare.exe
' The combined length of and 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 or , 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[/code]
Regards, AB


#18

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


#19

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


#20

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