GP SoftwareTwitter
Opus FAQsManualCommandsObjects

Command: Go-Relative (go to next/prev sibling folder)

scripting
commands
vbscript
jscript

#1

Alternative:

A slightly different version of this, re-written in JScript and with some extra functionality, can be found in Julianon's Jan 3, 2017 reply below.

Overview:

This script adds a GoRelative command.

  • GoRelative SIBLING=next will jump you directly to the next sibling of the current folder.

    In other words, it is like going up one level, selecting the next (or previous) folder to where you were, then entering it. Or like pushing the up or down cursor key on the folder tree.

  • GoRelative SIBLING=prev jumps to the previous sibling folder.

  • Add the WRAP argument to loop back to the first folder when you reach the last one (or vice versa), instead of stopping at the ends.

  • Add the SKIPHIDDEN argument to skip over hidden folders.

  • Folders that you cannot access are skipped automatically. (e.g. So you don't get error messages when cycling through folders below C:\.)

Installation:

  • Download Go_to_Relative.vbs.txt (4.5 KB)
  • Drag it to Preferences / Toolbars / Scripts.
  • With the script installed, you can create toolbar buttons or hotkeys which use the GoRelative command, as described in the overview section above.

Here are two pre-made buttons you can drag to a toolbar:

History:

  • 1.1 (15/May/2015): No longer skips empty folders.
  • 1.0 (11/May/2015): Initial version.

Limitations:

  • It decides the next or previous folder by listing the parent's folders in alphabetical order.
  • Only real paths are currently used, not virtual paths or localised names. With localised names, the order you visit directories may not match the order you're used to seeing; for example, the My Documents folder is really called Documents behind the scenes, so it will appear earlier in the cycle than you might expect, if you have Opus configured to use localised names.
  • If you are in the Desktop virtual folder, the script will act as if you are below C:\Users\<username>\Desktop instead, and cycle through the folders below C:\Users\<username>.
  • If you are in a library, the script will act as if you are below the library's real path.
  • Doesn't currently do anything at the root of a drive. It might cycle through drive letters in the future.
  • May only currently consider the first ~65000 siblings. (This limitation may no longer apply, although the script will be very slow with that many folders to work through anyway.)

Script code:

The script code from the download above is reproduced below. This is for people browsing the forum for scripting techniques. You do not need to care about this code if you just want to use the script.

option explicit

' Go to Relative
' (C) 2015 Leo Davidson
' 
' This is a script for Directory Opus.
' See http://www.gpsoft.com.au/DScripts/redirect.asp?page=scripts for development information.

' Called by Directory Opus to initialize the script
Function OnInit(initData)
	initData.name = "Go to Relative"
	initData.desc = "Adds command to navigate to sibling folders."
	initData.copyright = "(C) 2015 Leo Davidson"
	initData.version = "1.1"
	initData.default_enable = true

	Dim cmd

	Set cmd = initData.AddCommand
	cmd.name = "GoRelative"
	cmd.method = "OnGoRelative"
	cmd.desc = "Go to the next or previous sibling of the current folder."
	cmd.label = "GoRelative"
	cmd.template = "SIBLING/K[next,prev],WRAP/S,SKIPHIDDEN/S"
End Function

' Implement the GoRelative command
Function OnGoRelative(scriptCmdData)

	' Check arguments to see what we've been asked to do.

	If (scriptCmdData.Func.args.got_arg.SIBLING) Then
		OnGoSibling(scriptCmdData)
	End If

	' Other modes may be added here in the future.

End Function

Function OnGoSibling(scriptCmdData)

	' Check arguments to see if we're going to the next or previous sibling.

	Dim modeString, fNext, fWrap, fSkipHidden

	modeString = UCase(scriptCmdData.Func.args.SIBLING)
	If (modeString = "NEXT") Then
		fNext = True
	ElseIf (modeString = "PREV" Or modeString = "PREVIOUS") Then
		fNext = False
	Else
		DOpus.Output "GoRelative: SIBLING argument used with invalid parameter """ & modeString & """.", True
		Exit Function
	End If

	fWrap = scriptCmdData.Func.args.got_arg.WRAP
	fSkipHidden = scriptCmdData.Func.args.got_arg.SKIPHIDDEN

	' Get the parent folder, resolving aliases like /desktop while we're at it.

	Dim pathParent, stringCurrentPath
	Set pathParent = DOpus.FSUtil.Resolve(scriptCmdData.Func.sourcetab.path)
	stringCurrentPath = pathParent

	If (Not pathParent.test_parent) Then
		' If we're on a drive letter, find the next/prev drive.

		Dim driveNum
		driveNum = CInt(pathParent.drive)

		If (driveNum = 0) Then
			' No parent, and not a drive letter, so we give up.
			Exit Function
		End If
		
		' TODO: Hopping between drives not implemented yet.
	Else
		pathParent.Parent
	
		' List the sibling folders, then sort the list.

		Dim vecSiblings, folderEnum, folderItem, folderError
		Set vecSiblings = DOpus.Create.Vector
		Set folderEnum = DOpus.FSUtil.ReadDir(pathParent, False)

		If (CLng(folderEnum.error) <> 0) Then
			DOpus.Output "GoRelative: Error " & CLng(folderEnum.error) & " reading folder """ & pathParent & """.", True
			Exit Function
		End If

		Do While (Not folderEnum.complete)
			Set folderItem = folderEnum.Next
			
			If (folderItem.is_dir) Then
				vecSiblings.push_back(folderItem)
			End If
		Loop

		vecSiblings.sort
	
		' Find the current folder in the list.
		
		Dim curIdx, origIdx, total, fFound
		curIdx = CLng(0)
		total = CLng(vecSiblings.count)
		fFound = False

		Do While (curIdx < total)
			If (vecSiblings(curIdx) = stringCurrentPath) Then
				fFound = True
				Exit Do
			End If
			curIdx = curIdx + CLng(1)
		Loop
		
		If (Not fFound) Then
			Exit Function
		End If

		origIdx = curIdx

		Do While (True)
			If (fNext) Then
				curIdx = curIdx + CLng(1)
				If (curIdx = total) Then
					If (Not fWrap) Then
						Exit Function
					End If
					curIdx = CLng(0)
				End If
			Else
				If (curIdx = CLng(0)) Then
					If (Not fWrap) Then
						Exit Function
					End If
					curIdx = total - CLng(1)
				Else
					curIdx = curIdx - CLng(1)
				End If
			End If

			If (origIdx = curIdx) Then
				Exit Function ' We could not find another folder to go to.
			End If

			' Test we can access the folder, and skip it if not.
			Set folderEnum = DOpus.FSUtil.ReadDir(vecSiblings(curIdx), False)
			' If error is non-zero but complete is not set, it means we can read the dir
			' but may be unable to get information about some of its children, which is fine.
			
			folderError = CLng(folderEnum.error)
			
			' Error 18 is ERROR_NO_MORE_FILES which just means the folder is empty.
			If (folderError = CLng(0) Or folderError = CLng(18) Or Not folderEnum.complete) Then

				If (Not fSkipHidden) Then
					Exit Do ' This is the folder we want.
				End If

				' Check if the folder is hidden and skip it if it is.

				If ((vecSiblings(curIdx).attr And 2) = 0) Then
					Exit Do
				End If
			End If
		Loop
		
		scriptCmdData.func.command.RunCommand "Go PATH=""" & vecSiblings(curIdx) & """"
	End If

End Function

Multi-line file display toolbar with address on a dedicated line
External Control Codes seemingly not updated within a function on selection change
Button to navigate through dirs
External Control Codes seemingly not updated within a function on selection change
Select Next or Previous folder
#2

Wtf? It's unusable with that limitation! o)

It does not enter empty folders though, as the readdirs() returncode is 18 or something for them iirc. Maybe another option "SKIPEMPTY" makes sense, to make this behaviour more determined?

Anyway, this one is perfect for ctrl-doubleclicking the listers background (+shift = previous e.g.), to have it "nearby" the regular Go BACK/UP by mouse (I think). Thanks for doing this, I tried.. and it works real' good. o)


#3

Thank you very much, Leo. I had never thought about such a button, but once I had added it and started working, I immediately realised how much fiddling about it eliminated for me. I now have two beaut new hotkeys, one for "Next Sibling" and one for "Previous Sibling".

As tbone also asks, could you possibly not skip empty directories — or else provide a "NoSkipEmpty" switch (or make this the default and provide a "SkipEmpty" switch). For example, at the start of each year, I automatically generate various empty directories, which I then populate during the year as things come in. I am finding the buttons' behaviours confusing in this situation, because I don't know which directories are still empty, and so I don't know how many times to press the hotkey. I also can't get to some still-empty directory that I am actually aiming for.


#4

@julianon
I have a GoEx command in the making, that combines some other GO-related things and adds SKIPEMPTY and WRAPASK to this beauty.
I started to create rather multifunctional commands recently, as I lose oversight of what's in my script addins folder and because some of the contributions just don't evolve once published. They sometimes serve more as an example than something that is under maintanance, don't know what this one will end up though. If Leo is willing to improve it, you're just spoilt for choice I guess. o)


#5

I installed the script and created a button using the GORELATIVE command. Since I am not as sharp as you guys, all I found when I used the button was script errors. Please add a button with the script download. Thanks!


#6

What were the errors?


#7

Root post updated to v1.1. No longer skips empty folders.

Added two sample buttons, too.


#8

Thanks Leo. No problems using the updated script and the two buttons. Very nice. :thumbsup:


#9

Thanks again, Leo, for the changed behaviour with empty directories — it's perfect now as far as I am concerned. My brother has 65537 siblings, but I don't, so that limitation doesn't worry me.


#10

Leo's GoRelative, with no more scripting, allows two further very simple buttons that navigate parallel directory structures. They are also proving very useful to me, and I thought it may be worth posting them.

I have some systems of parallel directories based on years. For example, in my records, the directory 2015 has subdirectories
Banking, BankingStatements, Super, Tax, . . . and so do the directories 2014, 2013, 2012, . . . When I am in 2015\Tax, I often want to go directly to the previous parallel directory 2014\Tax, or further back to 2013\Tax, and then return to 2015\Tax.

The following button use GoRelative to go to the next parallel directory:

@Set TheDirectory={SourcePath|NoPath}
Go Up
GoRelative Sibling=Next Wrap SkipHidden
Go {$TheDirectory}

and changing "Next" to "Prev" gives a button that goes to the previous parallel directory. The last line throws an error if there is no parallel directory. The buttons are easily adapted to any deeper parallel nesting.


#11

A very useful script, good work.


#12

Dopus and @leo strikes again!

I just came to the forums to look/ask for exactly this type of button.
You are making my life a living hell! Because with the direction windows is going it's time to seriously think about moving on to linux, imho.

But how can I do that, when there is no DOPus on linux? Huh!!!???


#13

Very useful script Leo !

just found it, I will use it a lot.

@Julianon, I don't have that kind of directory structure, but i have to say the idea is great and should prove very useful to many :wink:


#14

Nice. I was looking for something like that for some time, decided to search for it and found it.


#15

I've taken the dangerous liberty of adding some bells and whistles to leo's script. Apologies, leo, in advance for my inevitable mistakes and misunderstandings. In particular, I'm not confident that I've understood the significance of all the checks in the code.

  1. I rewrote it in JScript because I don't know VBScript, and renamed it from Go_to_Relative.vbs to GoRelative.js to avoid confusion with leo's script.

  2. Drive-hopping is added. That is, if the script climbs to the root of a drive rather than a directory, then it will hop to the next or previous drive in alphabetical order.
    — This drivehopping ignores drives that are not ready, but otherwise does not distinguish between types of drives (one could easily add such code).
    — A new switch argument ChooseDrive of type /S triggers a dialogue for the user to choose the target drive, rather than have it chosen automatically.
    — Another new argument OmitDrives of type /K can avoid annoying things such as HP's naming of their backup drive as D: drive. For example,
    OmitDrives=D:,M:,Z: (OmitDrives=D,M,Z and OmitDrives=D:\,M:\,Z:\ will also work).

  3. More significantly, a new argument Depth, of type /O, allows automatic navigation through parallel directory structures. By itself or with an invalid value, it triggers a dialogue, otherwise it should have a positive whole number as its value.

    • Depth=1 is the default if the Depth argument is not mentioned. This makes the script behave as before.

    • Depth=2 climbs two steps instead of one up and then down the directory tree.

    For example, suppose that there are two directories:

    ...\Lessons\2016\Class09 and
    ...\Lessons\2017\Class09

    Suppose that one is working in the ...\Lessons\2017\Class09 directory, and wants to go to
    ...\Lessons\2016\Class09 to pick up something from last year's directory. The following two buttons swap between them:

    GoRelative Depth=2 Sibling=Previous (or Sibling=-1)
    GoRelative Depth=2 Sibling=Next (or Sibling=1)

  • Depth=3 extends this climb one more step.

    Suppose that there are two directories:

    ...\Lessons\2016\Class09\Quadratics and
    ...\Lessons\2017\Class09\Quadratics

    The following two buttons swap between them:

    GoRelative Sibling=Previous Depth=3
    GoRelative Sibling=Next Depth=3

    • And so on. Depth=4 would be for ...Lessons\2016\Class09\Quadratics\Tests.
  1. For depths greater than 1, the tree-climbing may comes to a drive prematurely. In this situation, the climbing stops, and drive-hopping begins.

  2. Once the depth is greater than 1, the parallel target directory may not exist. When this happens, the script goes as far back down the target tree as far as it can, then issues a warning message.

  3. The Sibling argument is now of type /O. The chosen sibling may be 1, 2, 3, ... away, which may be useful when the depth is greater than 1.

    • When the argument is absent (not recommended), it defaults to the Next sibling directory (or drive).

    • The argument Sibling can take any non-zero integer, where positive integers move forward that many items through the siblings, and negative numbers move backwards through the siblings.

    • The argument Sibling can also take, as it did before, the values Next (=1), Previous (=-1) or Prev, which move to the next or previous sibling.

    • By itself or with an invalid value, the argument Sibling triggers a dialogue.

    • The switch argument Wrap of type /S now uses modulo arithmetic in the obvious way.

    In particular, the command GoRelative with no Sibling argument is the same as GoRelative Sibling=Next or GoRelative Sibling=1.

  4. The default is now to include hidden files, but exclude system files. These defaults can be changed with two switches of type /S:

    • SkipHidden (unchanged) and IncludeSystem (new).
  5. In a DOpus thread a year or so ago, I posted a system using the keys F12, F11, F10 and F9, for opening directories in another tab, the other dual panel, or another single- or dual-panel lister. These methods now work consistently with the GoRelative commands.

    I had already implemented, and posted above, a less elaborate version of 'depth' using leo's unaltered script combined with ordinary DOpus commands. But the F12-F9 methods did not work consistently with these earlier 'depth' buttons, perhaps because of timing issues, and this was my original motivation for fiddling with leo's script.

    • A new switch UserDefinedGo, of type /S, now changes the final command from DOpus' normal Go command to the user-defined command A@Go used in the F12--F9 system. Obviously, ignore this switch entirely unless the F12--F9 system is already installed and running.

    [To recapitulate this system, before running the script:
    Press F12 to open the new folder in a new tab.
    Press F11 to open it in the dual panel.
    Press F12 and F11 to open it in a new tab of the dual panel.
    Press F10 to open it in a single-panel lister.
    Press F9 to open it in a dual-panel lister.
    See the earlier thread for the code to attach to F12-F9 and the corresponding menubar icons, where the hotkeys are arbitrary, or could be omitted.]


Examples (without the UserDefinedGo switch):

GoRelative Sibling=Next Wrap Depth=1 — Totally standard

GoRelative Sibling=Previous Depth ChooseDrive SkipHidden — Dialogues for the depth and the drive.

GoRelative Sibling Wrap Depth=2 OmitDrives=M:,Z: — Dialogue for the sibling, and Drives M: and Z: are omitted if drive-hopping is necessary.


Installation:

The file below is the adapted GoRelative.js script. I was not sure how to handle the copyright protocols, which may well need adjusting.

First, disable the old addon Go_to_Relative.vbs if it is installed, otherwise DOpus will have two conflicting commands GoRelative. The easiest way is to navigate to the addons directory and rename its extension to .vbsx — this is easily reversed in the event of blunders by me.

Download the new file GoRelative.js.txt below, open the scripts window by Preferences > Toolbars > Scripts, and drag the downloaded file to its right-hand panel.


External Control Codes seemingly not updated within a function on selection change
#16

Excellent work, gentlemen!

Is there a way to make this work from the standalone image viewer? This sequence works manually, but not from a viewer hot key:

Show VIEWERCMD=selectfile
GoRelative Sibling=Next Wrap Depth=1
Select FIRST
Show

#17

What you're doing won't work because those commands are all being run in the context of the viewer, not the file display you're trying to do things in.

Navigating the file display and/or changing the selection in the UI just to feed different files to commands is rarely a good idea, even when it does work. It's fragile to clicks you might do at the same time, and the timing of things, and also messes around with the file display as a side-effect of something else, when you might not want the file display to be messed around with. (In this case, the file display may not even be there, too.)

If you want to jump the viewer to the first file in the next folder, you could do that using a script which works out which file to display and tells the viewer to display it. That's the approach I'd go for. (I can write the script in a while, but probably won't have time before 12.10 is released as I'm flat-out finishing things for that right now.)


#18

Thanks for the explanation. A script would of course be nice, but no rush, 12.10 has definitely higher priority!


#19

Great one! Thanks for sharing.