// Command.FileGroupNavigation // (c) 2021 Felix // This is a script for Directory Opus. // See https://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 = "Command.Navigation.FileGroupNavigation"; initData.version = "v1.21 (2021.10.31)"; initData.copyright = "(c) 2021 Felix"; initData.url = "https://resource.dopus.com/t/command-filegroupnavigation-navigate-in-filegroups-previous-next/38735"; initData.desc = "Navigating between and inside of filegroups"; initData.default_enable = true; initData.min_version = "12.0"; initData.group = "Command"; var cmd = initData.AddCommand(); cmd.name = "FileGroupNavigation"; cmd.method = "OnFileGroupNavigation"; cmd.desc = "Navigating between and inside of filegroups"; cmd.label = "OnFileGroupNavigation"; //https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Argument_Types.htm/S //https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Internal_Command_Arguments.htm cmd.template = "NAVIGATE/K[previous,next],NAVIGATETARGET/K[start,middle,end],STEP/N,COLLAPSEOTHERS/S,GORUPJUMPLIST/S,ITEMJUMPLIST/S,SHOWFILECOUNT/S,SHOWFULLITEMPATH/S"; cmd.hide = false; cmd.icon = "groupby"; } // Implement the FileGroupNavigation command function OnFileGroupNavigation(scriptCmdData) { var args = scriptCmdData.func.args; var direction = 0; //the direction to go, prev/next, implied via sign var collapseOthers = args.got_arg.COLLAPSEOTHERS; //collapse non focused groups var stepSize = 1; //how many groups to skip, default is 1 var itemSelectorIndex = 0; //select first item in group if(args.got_arg.GORUPJUMPLIST) { ShowGroupJumplist(scriptCmdData, args.got_arg.SHOWFILECOUNT); return; //Using jumplist does exclude using the prev/next buttons so exit here } if(args.got_arg.ITEMJUMPLIST) { ShowItemJumplist(scriptCmdData, args.got_arg.SHOWFULLITEMPATH); return; //Using jumplist does exclude using the prev/next buttons so exit here } if(args.got_arg.NAVIGATE) { var navigateArg = args.NAVIGATE.toLowerCase(); if(navigateArg == "previous") direction = -1; else if(navigateArg == "next") direction = 1; if(args.got_arg.NAVIGATETARGET) { var navigateTarget = args.NAVIGATETARGET.toLowerCase(); if(navigateTarget == "start") itemSelectorIndex = 0; else if(navigateTarget == "middle") itemSelectorIndex = 1; else if(navigateTarget == "end") itemSelectorIndex = 2; } } if(args.got_arg.STEP) { try { stepSize = parseInt(args.STEP); } catch(e) { stepSize = 1; Log(e, true); } } if(direction != 0) GroupNavigation(scriptCmdData.func, direction * stepSize, collapseOthers, ItemSelectors[itemSelectorIndex]); } // //-------------------------- Group navigation // //Go Next/Previous or with step size functionality function GroupNavigation(func, direction, collapseOthers, itemSelector) { var tab = func.sourcetab; tab.Update(); var groups = tab.filegroups; if(groups.count > 0) { var focusedItem = tab.GetFocusItem(); var containingGroup = focusedItem.filegroup; if(containingGroup != null) { var groupIndex = GetGroupIndex(containingGroup, groups); if(groupIndex > -1) { var nextGroupResult = GetNextGroup(groups, groupIndex, direction); var nextGroup = nextGroupResult.group; GoToGroup(nextGroup, collapseOthers, itemSelector, func); } } } } //Select the first item in the desired group and focus it function GoToGroup(nextGroup, collapseOthers, itemSelector, func) { var tab = func.sourcetab; var cmd = func.command; var nextSelectedItem = itemSelector(nextGroup); ClearCommand(cmd); tab.Update(); if(collapseOthers) { ClearCommand(cmd); cmd.AddLine("Go GROUPCOLLAPSE *"); cmd.Run(); } ClearCommand(cmd); cmd.RunCommand("Go GROUPEXPAND \"" + nextGroup + "\""); //Make sure it is definitly expanded ClearCommand(cmd); tab.Update(); FocusItem(nextSelectedItem, cmd, tab); } //Select item and bring to focus function FocusItem(item, cmd, tab) { cmd.AddFile(item); cmd.AddLine("SELECT NONE"); cmd.AddLine("Select FROMSCRIPT EXACT MAKEVISIBLE SETFOCUS"); cmd.Run(); tab.Update(); } // //-------------------------- Jumplist // //Returns first item of group for GoToGroup function FirstItemInGroupSelector(group) { return group.members(0); } //Returns middle item of group for GoToGroup function MiddleItemInGroupSelector(group) { return group.members(group.count > 0 ? group.count / 2 : 0); } //Returns last item of group for GoToGroup function LastItemInGroupSelector(group) { return group.members(group.count > 0 ? group.count - 1 : 0); } //Used for Jumplist dialog var ItemSelectors = [ FirstItemInGroupSelector, MiddleItemInGroupSelector, LastItemInGroupSelector ]; //Show a list of all groups in this tab and go to the selected one function ShowGroupJumplist(cmdData, showItemCount) { var tab = cmdData.func.sourcetab; var groups = tab.filegroups; var jumplistDlg = cmdData.func.Dlg; var groupVector = DOpus.Create.Vector(); var groupArray = []; //Convert groups to array for easier access var menuVector = DOpus.Create.Vector(); var groupIndex = -1; //Has an item a focus? Then mark the containing group bold if(!groups.count) return;//Maybe some information here? var focusedItem = tab.selected.count > 0 ? tab.selected(0) : null; //get first selected file if(focusedItem) { var containingGroup = focusedItem.filegroup; if(containingGroup) { groupIndex = GetGroupIndex(containingGroup, groups); } } var index = 0; for (var groupEnum = new Enumerator(groups); !groupEnum.atEnd(); groupEnum.moveNext()) { var groupItem = groupEnum.item(); if(groupItem.count > 0) { var itemName = showItemCount ? groupItem + " (" + groupItem.count + ")" : String(groupItem); groupVector.push_back(itemName); groupArray.push(groupItem); if(groupIndex > -1) { if(index == groupIndex) menuVector.push_back(1); //Mark bold else menuVector.push_back(0); } else menuVector.push_back(0); } index++; } jumplistDlg.choices = groupVector; jumplistDlg.selection = (groupIndex > -1) ? groupIndex : 0; jumplistDlg.title = 'Jump to group'; jumplistDlg.message = 'Select the group you want to jump to'; jumplistDlg.buttons = "Jump to start|Jump to middle|Jumpt to end|Cancel"; var jumpListResult = jumplistDlg.Show(); if(jumpListResult > 0) { var selectedGroupIndex = jumplistDlg.selection; var group = GetGroupByName(groupArray[selectedGroupIndex], groups); if(group != null) { GoToGroup(group, true, ItemSelectors[jumpListResult - 1], cmdData.func); Log(selectedGroupIndex); } } } //Display a dialog to jump to file in the current group function ShowItemJumplist(cmdData, showFullItemPath) { var tab = cmdData.func.sourcetab; var groups = tab.filegroups; //if(groups.count == 1 && groups(0).count == 0) if(!groups.count) //undefined so no groups are shown / Default group return; var jumplistDlg = cmdData.func.Dlg; var itemVector = DOpus.Create.Vector(); var itemArray = []; //Convert groups to array for easier access var groupIndex = -1; //Has an item a focus? Then mark the containing group bold //var focusedItem = tab.GetFocusItem(); //PROBABLY WRONG var focusedItem = tab.selected.count > 0 ? tab.selected(0) : null; //get first selected file if(focusedItem) { var containingGroup = focusedItem.filegroup; if(containingGroup != null) { groupIndex = GetGroupIndex(containingGroup, groups); var items = containingGroup.members; for(var i = 0; i < items.count; i++) { itemVector.push_back(showFullItemPath ? items(i) : items(i).name); itemArray.push(items(i)); } itemVector.sort(); itemArray.sort(); jumplistDlg.choices = itemVector; jumplistDlg.selection = ArrayIndexOf(itemArray, focusedItem); jumplistDlg.title = 'Jump to file'; jumplistDlg.message = "Select the file you want to jump in the group '" + containingGroup + "'"; jumplistDlg.buttons = "OK|Cancel"; var jumpListResult = jumplistDlg.Show(); if(jumpListResult > 0) { var selectedItemIndex = jumplistDlg.selection; var selectedItem = itemArray[selectedItemIndex]; if(selectedItem) { FocusItem(selectedItem, cmdData.func.Command, tab); } } } } } // //-------------------------- Helper methods // //clear the command for reuse function ClearCommand(cmd) { cmd.Clear(); cmd.ClearFailed(); cmd.ClearFiles(); } //Get the index of the current group of the tabs group function GetGroupIndex(group, groups) { var index = 0; for (var groupEnum = new Enumerator(groups); !groupEnum.atEnd(); groupEnum.moveNext()) { var groupItem = groupEnum.item(); if(groupItem.id === group.id) return index; index++; } return -1; // not found } //Get a group object by name function GetGroupByName(groupName, groups) { for (var groupEnum = new Enumerator(groups); !groupEnum.atEnd(); groupEnum.moveNext()) { var groupItem = groupEnum.item(); if(String(groupName) == String(groupItem)) return groupItem; } return null; // not found } //get the next group according to stepsize (default 1). Direction (forward/backward) is defined by the sign function GetNextGroup(groups, currentIndex, navigationDirection) { var index = mod((currentIndex + navigationDirection), groups.count); var group = groups(index); while(group.count == 0) //to prevent default group, maybe better checking for != null? { index = mod((index + navigationDirection), groups.count); group = groups(index); } return {group: group, index: index}; } function ArrayIndexOf(array, item) { for(var i = 0; i < array.length; i++) if(String(array[i].path) == String(item.path)) return i; return -1; } //https://stackoverflow.com/questions/4467539/javascript-modulo-gives-a-negative-result-for-negative-numbers //Allowing negative mod for looping in arrays function mod(n, m) { return ((n % m) + m) % m; } function Log(msg, e) { DOpus.output(String(msg), e || false); }