1// Copyright (C) 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {exists} from '../base/utils'; 16import {Actions} from '../common/actions'; 17import {getLegacySelection} from '../common/state'; 18 19import {Flow, globals} from './globals'; 20import {focusHorizontalRange, verticalScrollToTrack} from './scroll_helper'; 21 22type Direction = 'Forward' | 'Backward'; 23 24// Search |boundFlows| for |flowId| and return the id following it. 25// Returns the first flow id if nothing was found or |flowId| was the last flow 26// in |boundFlows|, and -1 if |boundFlows| is empty 27function findAnotherFlowExcept(boundFlows: Flow[], flowId: number): number { 28 let selectedFlowFound = false; 29 30 if (boundFlows.length === 0) { 31 return -1; 32 } 33 34 for (const flow of boundFlows) { 35 if (selectedFlowFound) { 36 return flow.id; 37 } 38 39 if (flow.id === flowId) { 40 selectedFlowFound = true; 41 } 42 } 43 return boundFlows[0].id; 44} 45 46// Change focus to the next flow event (matching the direction) 47export function focusOtherFlow(direction: Direction) { 48 const currentSelection = getLegacySelection(globals.state); 49 if (!currentSelection || currentSelection.kind !== 'SLICE') { 50 return; 51 } 52 const sliceId = currentSelection.id; 53 if (sliceId === -1) { 54 return; 55 } 56 57 const boundFlows = globals.connectedFlows.filter( 58 (flow) => 59 (flow.begin.sliceId === sliceId && direction === 'Forward') || 60 (flow.end.sliceId === sliceId && direction === 'Backward'), 61 ); 62 63 if (direction === 'Backward') { 64 const nextFlowId = findAnotherFlowExcept( 65 boundFlows, 66 globals.state.focusedFlowIdLeft, 67 ); 68 globals.dispatch(Actions.setHighlightedFlowLeftId({flowId: nextFlowId})); 69 } else { 70 const nextFlowId = findAnotherFlowExcept( 71 boundFlows, 72 globals.state.focusedFlowIdRight, 73 ); 74 globals.dispatch(Actions.setHighlightedFlowRightId({flowId: nextFlowId})); 75 } 76} 77 78// Select the slice connected to the flow in focus 79export function moveByFocusedFlow(direction: Direction): void { 80 const currentSelection = getLegacySelection(globals.state); 81 if (!currentSelection || currentSelection.kind !== 'SLICE') { 82 return; 83 } 84 85 const sliceId = currentSelection.id; 86 const flowId = 87 direction === 'Backward' 88 ? globals.state.focusedFlowIdLeft 89 : globals.state.focusedFlowIdRight; 90 91 if (sliceId === -1 || flowId === -1) { 92 return; 93 } 94 95 // Find flow that is in focus and select corresponding slice 96 for (const flow of globals.connectedFlows) { 97 if (flow.id === flowId) { 98 const flowPoint = direction === 'Backward' ? flow.begin : flow.end; 99 const trackKeyByTrackId = globals.trackManager.trackKeyByTrackId; 100 const trackKey = trackKeyByTrackId.get(flowPoint.trackId); 101 if (trackKey) { 102 globals.setLegacySelection( 103 { 104 kind: 'SLICE', 105 id: flowPoint.sliceId, 106 trackKey, 107 table: 'slice', 108 }, 109 { 110 clearSearch: true, 111 pendingScrollId: flowPoint.sliceId, 112 switchToCurrentSelectionTab: true, 113 }, 114 ); 115 } 116 } 117 } 118} 119 120export async function findCurrentSelection() { 121 const selection = getLegacySelection(globals.state); 122 if (selection === null) return; 123 124 const range = await globals.findTimeRangeOfSelection(); 125 if (exists(range)) { 126 focusHorizontalRange(range.start, range.end); 127 } 128 129 if (selection.trackKey) { 130 verticalScrollToTrack(selection.trackKey, true); 131 } 132} 133