// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. 'use strict'; /** * @fileoverview Code for the viewport. */ base.require('base.events'); base.require('base.guid'); base.require('base.range'); base.exportTo('tracing', function() { function SelectionSliceHit(track, slice) { this.track = track; this.slice = slice; } SelectionSliceHit.prototype = { get modelObject() { return this.slice; }, get selected() { return this.slice.selected; }, set selected(v) { this.slice.selected = v; }, addBoundsToRange: function(range) { range.addValue(this.slice.start); range.addValue(this.slice.end); } }; function SelectionCounterSampleHit(track, counter, sampleIndex) { this.track = track; this.counter = counter; this.sampleIndex = sampleIndex; } SelectionCounterSampleHit.prototype = { get modelObject() { return this.sampleIndex; }, get selected() { return this.track.selectedSamples[this.sampleIndex] == true; }, set selected(v) { if (v) this.track.selectedSamples[this.sampleIndex] = true; else this.track.selectedSamples[this.sampleIndex] = false; }, addBoundsToRange: function(range) { if (!this.track.timestamps) return; range.addValue(this.track.timestamps[this.sampleIndex]); } }; function SelectionObjectSnapshotHit(track, objectSnapshot) { this.track = track; this.objectSnapshot = objectSnapshot; } SelectionObjectSnapshotHit.prototype = { get modelObject() { return this.objectSnapshot; }, get selected() { return this.objectSnapshot.selected; }, set selected(v) { this.objectSnapshot.selected = v; }, addBoundsToRange: function(range) { range.addValue(this.objectSnapshot.ts); } }; function SelectionObjectInstanceHit(track, objectInstance) { this.track = track; this.objectInstance = objectInstance; } SelectionObjectInstanceHit.prototype = { get modelObject() { return this.objectInstance; }, get selected() { return this.objectInstance.selected; }, set selected(v) { this.objectInstance.selected = v; }, addBoundsToRange: function(range) { range.addRange(this.objectInstance.bounds); } }; var HIT_TYPES = [ { constructor: SelectionSliceHit, name: 'slice', pluralName: 'slices' }, { constructor: SelectionCounterSampleHit, name: 'counterSample', pluralName: 'counterSamples' }, { constructor: SelectionObjectSnapshotHit, name: 'objectSnapshot', pluralName: 'objectSnapshots' }, { constructor: SelectionObjectInstanceHit, name: 'objectInstance', pluralName: 'objectInstances' } ]; /** * Represents a selection within a and its associated set of tracks. * @constructor */ function Selection(opt_hits) { this.bounds_dirty_ = true; this.bounds_ = new base.Range(); this.length_ = 0; this.guid_ = base.GUID.allocate(); if (opt_hits) this.pushHits(opt_hits); } Selection.prototype = { __proto__: Object.prototype, get bounds() { if (this.bounds_dirty_) { this.bounds_.reset(); for (var i = 0; i < this.length_; i++) { var hit = this[i]; hit.addBoundsToRange(this.bounds_); } this.bounds_dirty_ = false; } return this.bounds_; }, get duration() { if (this.bounds_.isEmpty) return 0; return this.bounds_.max - this.bounds_.min; }, get length() { return this.length_; }, get guid() { return this.guid_; }, clear: function() { for (var i = 0; i < this.length_; ++i) delete this[i]; this.length_ = 0; this.bounds_dirty_ = true; }, pushHit: function(hit) { this.push_(hit); }, pushHits: function(hits) { for (var i = 0; i < hits.length; i++) this.pushHit(hits[i]); }, push_: function(hit) { this[this.length_++] = hit; this.bounds_dirty_ = true; return hit; }, addSlice: function(track, slice) { return this.push_(new SelectionSliceHit(track, slice)); }, addCounterSample: function(track, counter, sampleIndex) { return this.push_( new SelectionCounterSampleHit( track, counter, sampleIndex)); }, addObjectSnapshot: function(track, objectSnapshot) { return this.push_( new SelectionObjectSnapshotHit(track, objectSnapshot)); }, addObjectInstance: function(track, objectInstance) { return this.push_( new SelectionObjectInstanceHit(track, objectInstance)); }, addSelection: function(selection) { for (var i = 0; i < selection.length; i++) this.push_(selection[i]); }, subSelection: function(index, count) { count = count || 1; var selection = new Selection(); selection.bounds_dirty_ = true; if (index < 0 || index + count > this.length_) throw new Error('Index out of bounds'); for (var i = index; i < index + count; i++) selection.push_(this[i]); return selection; }, getCounterSampleHitsAsSelection: function() { var selection = new Selection(); this.enumHitsOfType(SelectionCounterSampleHit, selection.push_.bind(selection)); return selection; }, getSliceHitsAsSelection: function() { var selection = new Selection(); this.enumHitsOfType(SelectionSliceHit, selection.push_.bind(selection)); return selection; }, getHitsOrganizedByType: function() { var hits = {}; HIT_TYPES.forEach(function(hitType) { hits[hitType.pluralName] = new Selection(); }); for (var i = 0; i < this.length_; i++) { var hit = this[i]; HIT_TYPES.forEach(function(hitType) { if (hit instanceof hitType.constructor) hits[hitType.pluralName].push_(hit); }); } return hits; }, enumHitsOfType: function(type, func) { for (var i = 0; i < this.length_; i++) if (this[i] instanceof type) func(this[i]); }, getNumSliceHits: function() { var numHits = 0; this.enumHitsOfType(SelectionSliceHit, function(hit) { numHits++; }); return numHits; }, getNumCounterHits: function() { var numHits = 0; this.enumHitsOfType(SelectionCounterSampleHit, function(hit) { numHits++; }); return numHits; }, getNumObjectSnapshotHits: function() { var numHits = 0; this.enumHitsOfType(SelectionObjectSnapshotHit, function(hit) { numHits++; }); return numHits; }, getNumObjectInstanceHits: function() { var numHits = 0; this.enumHitsOfType(SelectionObjectInstanceHit, function(hit) { numHits++; }); return numHits; }, map: function(fn) { for (var i = 0; i < this.length_; i++) fn(this[i]); }, /** * Helper for selection previous or next. * @param {boolean} forwardp If true, select one forward (next). * Else, select previous. * @return {boolean} true if current selection changed. */ getShiftedSelection: function(offset) { var newSelection = new Selection(); for (var i = 0; i < this.length_; i++) { var hit = this[i]; hit.track.addItemNearToProvidedHitToSelection( hit, offset, newSelection); } if (newSelection.length == 0) return undefined; return newSelection; } }; function createSelectionFromObjectAndView(obj, opt_view) { // TODO: fill in the track intelligently by finding the TimelineView, then // finding the right track based on the provided object. var track = undefined; var selection = new Selection(); if (obj instanceof tracing.trace_model.Slice) selection.addSlice(track, obj); else if (obj instanceof tracing.trace_model.ObjectSnapshot) selection.addObjectSnapshot(track, obj); else if (obj instanceof tracing.trace_model.ObjectInstance) selection.addObjectInstance(track, obj); else throw new Error('Unrecognized selection type'); return selection; } return { SelectionSliceHit: SelectionSliceHit, SelectionCounterSampleHit: SelectionCounterSampleHit, SelectionObjectSnapshotHit: SelectionObjectSnapshotHit, SelectionObjectInstanceHit: SelectionObjectInstanceHit, Selection: Selection, createSelectionFromObjectAndView: createSelectionFromObjectAndView }; });