Closing Duplicate Tabs in Directory Opus

 ·   ·  ☕ 8 min read
🏷️
This page looks best with JavaScript enabled

One of the standout features of Directory Opus is its capability to manage multiple listers and a multitude of open tabs. However, this flexibility can quickly lead to a cluttered workspace if redundant tabs aren’t regularly closed.

The script helps simplify tab management by automatically identifying and closing duplicate tabs, ensuring that only unique and necessary tabs remain open. This allows users to maintain a cleaner, more efficient workspace and reduces the time spent manually sorting through tabs.

For power users and anyone looking to eliminate the clutter, this script is a game-changer, saving time and improving the overall experience with Directory Opus.

The Script

The following JavaScript code clears duplicates among the tabs, while ensuring that source, destination, and locked tabs are not closed. Here’s an overview of the script:

  • The main function triggers on click, prepares the command, and prioritizes tabs based on whether they’re currently selected, locked, or both.
  • Duplicates are flagged and then closed from right to left.
  • Selected and locked tabs are preserved.
  • Temporary flags are used to identify and manage duplicates.

Detailed Breakdown

Main Function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function OnClick(clickData) {
  DOpus.ClearOutput();
  var cmd = clickData.func.command;
  cmd.deselect = false;

  var lister = DOpus.listers.lastactive;
  var sortedTabs = FilterAndSort(lister.tabs);

  CloseDuplicateTabs(cmd, sortedTabs);
}
  • Line 2: Clears the output pane in Directory Opus.
  • Line 3: Retrieves the command object from the click data.
  • Line 4: Ensures the clicked item is not deselected.
  • Line 6: Gets the last active lister (main window) in Directory Opus.
  • Line 7: Filters and sorts the tabs of the active lister.
  • Line 9: Calls a function to close duplicate tabs.

Prioritize Selected and Locked Tabs

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function FilterAndSort(collection) {
  var filteredArray = [];

  for (var i = 0; i < collection.count; i++) {
    filteredArray.push(collection[i]);
  }

  filteredArray.sort(function(a, b) {
    if ((a.source && !b.source) || (a.dest && !b.dest)) return -1;
    if ((!a.source && b.source) || (!a.dest && b.dest)) return 1;
    if (a.lock !== "off" && b.lock === "off") return -1;
    if (a.lock === "off" && b.lock !== "off") return 1;
    return 0;
  });

  return filteredArray;
}
  • Lines 13-17: Converts the collection of tabs into an array.
  • Lines 19-25: Sorts the tabs based on the following criteria:
    1. Source or Destination: Selected (Source or destination) tabs are prioritized.
    2. Locked Status: Locked tabs are given priority over unlocked ones.
    3. Default Order: Tabs are left in their current order if all other criteria are equal.

Close Duplicate Tabs

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function CloseDuplicateTabs(cmd, tabs) {
  CreateDuplicateFlag(tabs);

  for (var i = 0; i < tabs.length; i++) {
    for (var j = tabs.length - 1; j > i; j--) {
      var a = tabs[i];
      var b = tabs[j];
      if (!b.source && !b.dest && b.lock === "off" && a.path.def_value === b.path.def_value) {
        tabs[j].vars.Set("isDuplicate", true);
      }
    }
  }

  for (var i = tabs.length - 1; i >= 0; i--) {
    var tab = tabs[i];
    if (tab.vars.Get("isDuplicate") === true) {
      cmd.RunCommand("Go TABCLOSE=" + tab);
    }
  }

  DeleteDuplicateFlag(tabs);
}
  • Line 31: Calls a function to add a temporary flag to each tab. This is exclusively used to distinguish between duplicate and non-duplicate tabs.
  • Lines 33-41: Compares each tab with every other tab to find duplicates, excluding source, destination, and locked tabs.
  • Lines 43-48: Closes tabs marked as duplicates, starting from the end of the list.
  • Line 50: Calls a function to remove the temporary flag from each tab.

Temporary Duplicate Flag Handling

53
54
55
56
57
58
59
60
61
62
63
function CreateDuplicateFlag(tabs) {
  for (var i = 0; i < tabs.length; i++) {
    tabs[i].vars.Set("isDuplicate", false);
  }
}

function DeleteDuplicateFlag(tabs) {
  for (var i = 0; i < tabs.length; i++) {
    tabs[i].vars.Delete("isDuplicate");
  }
}
  • Lines 54-56: Adds an isDuplicate flag set to false for each tab.
  • Lines 60-62: Removes the isDuplicate flag from each tab.

The Complete Script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function OnClick(clickData) {
  DOpus.ClearOutput();
  var cmd = clickData.func.command;
  cmd.deselect = false;

  var lister = DOpus.listers.lastactive;
  var sortedTabs = FilterAndSort(lister.tabs);

  CloseDuplicateTabs(cmd, sortedTabs);
}

function FilterAndSort(collection) {
  var filteredArray = [];

  for (var i = 0; i < collection.count; i++) {
    filteredArray.push(collection[i]);
  }

  filteredArray.sort(function(a, b) {
    if ((a.source && !b.source) || (a.dest && !b.dest)) return -1;
    if ((!a.source && b.source) || (!a.dest && b.dest)) return 1;
    if (a.lock !== "off" && b.lock === "off") return -1;
    if (a.lock === "off" && b.lock !== "off") return 1;
    return 0;
  });

  return filteredArray;
}

function CloseDuplicateTabs(cmd, tabs) {
  CreateDuplicateFlag(tabs);

  for (var i = 0; i < tabs.length; i++) {
    for (var j = tabs.length - 1; j > i; j--) {
      var a = tabs[i];
      var b = tabs[j];
      if (!b.source && !b.dest && b.lock === "off" && a.path.def_value === b.path.def_value) {
        tabs[j].vars.Set("isDuplicate", true);
      }
    }
  }

  for (var i = tabs.length - 1; i >= 0; i--) {
    var tab = tabs[i];
    if (tab.vars.Get("isDuplicate") === true) {
      cmd.RunCommand("Go TABCLOSE=" + tab);
    }
  }

  DeleteDuplicateFlag(tabs);
}

function CreateDuplicateFlag(tabs) {
  for (var i = 0; i < tabs.length; i++) {
    tabs[i].vars.Set("isDuplicate", false);
  }
}

function DeleteDuplicateFlag(tabs) {
  for (var i = 0; i < tabs.length; i++) {
    tabs[i].vars.Delete("isDuplicate");
  }
}

The Command File

To make adding this command to your Directory Opus installation simpler, the above script has been wrapped into a handy “Close Duplicate Tabs” Directory Command File (DCF), which includes a label, tooltip, and icon. You can double-click the file to execute it directly, or customize the Directory Opus toolbar and drop it there for easier access.

You can download the ready-to-use file, or create it yourself using the following XML code. Just be sure to save the new file with the .dcf file extension so that it’s properly handled by Directory Opus."

now you know
Command Iconas it appears in Directory Opus 13
Close Duplicate Tabs.dcf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0"?>
<button backcol="none" display="icon" label_pos="right" textcol="none">
  <label>Close Duplicate Tabs</label>
  <tip>Close duplicate tabs in the active lister</tip>
  <icon1>#closetab</icon1>
  <function type="script">
    <instruction>@script JScript</instruction>
    <instruction>function OnClick(clickData) {</instruction>
    <instruction>  DOpus.ClearOutput();</instruction>
    <instruction>  var cmd = clickData.func.command;</instruction>
    <instruction>  cmd.deselect = false;</instruction>
    <instruction />
    <instruction>  var lister = DOpus.listers.lastactive;</instruction>
    <instruction>  var sortedTabs = FilterAndSort(lister.tabs);</instruction>
    <instruction />
    <instruction>  CloseDuplicateTabs(cmd, sortedTabs);</instruction>
    <instruction>}</instruction>
    <instruction />
    <instruction>function FilterAndSort(collection) {</instruction>
    <instruction>  var filteredArray = [];</instruction>
    <instruction />
    <instruction>  // Convert collection to array</instruction>
    <instruction>  for (var i = 0; i &lt; collection.count; i++) {</instruction>
    <instruction>    filteredArray.push(collection[i]);</instruction>
    <instruction>  }</instruction>
    <instruction />
    <instruction>  // Sort the array</instruction>
    <instruction>  filteredArray.sort(function(a, b) {</instruction>
    <instruction>    // First criteria: source or dest</instruction>
    <instruction>    if ((a.source &amp;&amp; !b.source) || (a.dest &amp;&amp; !b.dest)) return -1;</instruction>
    <instruction>    if ((!a.source &amp;&amp; b.source) || (!a.dest &amp;&amp; b.dest)) return 1;</instruction>
    <instruction />
    <instruction>    // Second criteria: locked</instruction>
    <instruction>    if (a.lock !== &quot;off&quot; &amp;&amp; b.lock === &quot;off&quot;) return -1;</instruction>
    <instruction>    if (a.lock === &quot;off&quot; &amp;&amp; b.lock !== &quot;off&quot;) return 1;</instruction>
    <instruction />
    <instruction>    // Default order if all criteria are equal</instruction>
    <instruction>    return 0;</instruction>
    <instruction>  });</instruction>
    <instruction />
    <instruction>  return filteredArray;</instruction>
    <instruction>}</instruction>
    <instruction />
    <instruction>function CloseDuplicateTabs(cmd, tabs) {</instruction>
    <instruction>  // A temporary &quot;isDuplicate&quot; flag is added to each tab during the</instruction>
    <instruction>  // removal process</instruction>
    <instruction>  CreateDuplicateFlag(tabs);</instruction>
    <instruction />
    <instruction>  // Identify the duplicate tabs, while always retaining source, dest, and</instruction>
    <instruction>  // locked tabs</instruction>
    <instruction>  for (var i = 0; i &lt; tabs.length; i++) {</instruction>
    <instruction>    for (var j = tabs.length - 1; j &gt; i; j--) {</instruction>
    <instruction>      var a = tabs[i];</instruction>
    <instruction>      var b = tabs[j];</instruction>
    <instruction>      if (!b.source &amp;&amp; !b.dest &amp;&amp; b.lock === &quot;off&quot; &amp;&amp; a.path.def_value === b.path.def_value) {</instruction>
    <instruction>        tabs[j].vars.Set(&quot;isDuplicate&quot;, true);</instruction>
    <instruction>      }</instruction>
    <instruction>    }</instruction>
    <instruction>  }</instruction>
    <instruction />
    <instruction>  // Close the duplicate tabs from the end of the collection first. Since</instruction>
    <instruction>  // the full tab list includes all left tabs followed by all right tabs,</instruction>
    <instruction>  // right tab duplicates will be removed first.</instruction>
    <instruction>  for (var i = tabs.length - 1; i &gt;= 0; i--) {</instruction>
    <instruction>    var tab = tabs[i];</instruction>
    <instruction>    if (tab.vars.Get(&quot;isDuplicate&quot;) === true) {</instruction>
    <instruction>      cmd.RunCommand(&quot;Go TABCLOSE=&quot; + tab);</instruction>
    <instruction>    }</instruction>
    <instruction>  }</instruction>
    <instruction />
    <instruction>  // Clear the temporary &quot;isDuplicate&quot; flag from the tabs</instruction>
    <instruction>  DeleteDuplicateFlag(tabs);</instruction>
    <instruction>}</instruction>
    <instruction />
    <instruction>function CreateDuplicateFlag(tabs) {</instruction>
    <instruction>  for (var i = 0; i &lt; tabs.length; i++) {</instruction>
    <instruction>    tabs[i].vars.Set(&quot;isDuplicate&quot;, false);</instruction>
    <instruction>  }</instruction>
    <instruction>}</instruction>
    <instruction />
    <instruction>function DeleteDuplicateFlag(tabs) {</instruction>
    <instruction>  for (var i = 0; i &lt; tabs.length; i++) {</instruction>
    <instruction>    tabs[i].vars.Delete(&quot;isDuplicate&quot;);</instruction>
    <instruction>  }</instruction>
    <instruction>}</instruction>
  </function>
</button>

By using this script, you can quickly close duplicate tabs and eliminate unnecessary clutter from your Directory Opus open tab list.

End of Line.