1<!DOCTYPE html>
2<!--
3Copyright (c) 2015 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/color_scheme.html">
9<link rel="import" href="/tracing/base/sorted_array_utils.html">
10<link rel="import" href="/tracing/model/proxy_selectable_item.html">
11<link rel="import" href="/tracing/ui/base/event_presenter.html">
12<link rel="import" href="/tracing/ui/base/heading.html">
13<link rel="import" href="/tracing/ui/base/ui.html">
14<link rel="import" href="/tracing/ui/tracks/track.html">
15
16<style>
17.letter-dot-track {
18  height: 18px;
19}
20</style>
21
22<script>
23'use strict';
24
25tr.exportTo('tr.ui.tracks', function() {
26  var EventPresenter = tr.ui.b.EventPresenter;
27  var SelectionState = tr.model.SelectionState;
28
29  /**
30   * A track that displays an array of dots with filled letters inside them.
31   * @constructor
32   * @extends {Track}
33   */
34  var LetterDotTrack = tr.ui.b.define(
35      'letter-dot-track', tr.ui.tracks.Track);
36
37  LetterDotTrack.prototype = {
38    __proto__: tr.ui.tracks.Track.prototype,
39
40    decorate: function(viewport) {
41      tr.ui.tracks.Track.prototype.decorate.call(this, viewport);
42      this.classList.add('letter-dot-track');
43      this.items_ = undefined;
44
45      this.heading_ = document.createElement('tr-ui-heading');
46      this.appendChild(this.heading_);
47    },
48
49    set heading(heading) {
50      this.heading_.heading = heading;
51    },
52
53    get heading() {
54      return this.heading_.heading;
55    },
56
57    set tooltip(tooltip) {
58      this.heading_.tooltip = tooltip;
59    },
60
61    get items() {
62      return this.items_;
63    },
64
65    set items(items) {
66      this.items_ = items;
67      this.invalidateDrawingContainer();
68    },
69
70    get height() {
71      return window.getComputedStyle(this).height;
72    },
73
74    set height(height) {
75      this.style.height = height;
76    },
77
78    get dumpRadiusView() {
79      return 7 * (window.devicePixelRatio || 1);
80    },
81
82    draw: function(type, viewLWorld, viewRWorld) {
83      if (this.items_ === undefined)
84        return;
85      switch (type) {
86        case tr.ui.tracks.DrawType.GENERAL_EVENT:
87          this.drawLetterDots_(viewLWorld, viewRWorld);
88          break;
89      }
90    },
91
92    drawLetterDots_: function(viewLWorld, viewRWorld) {
93      var ctx = this.context();
94      var pixelRatio = window.devicePixelRatio || 1;
95
96      var bounds = this.getBoundingClientRect();
97      var height = bounds.height * pixelRatio;
98      var halfHeight = height * 0.5;
99      var twoPi = Math.PI * 2;
100
101      // Culling parameters.
102      var dt = this.viewport.currentDisplayTransform;
103      var dumpRadiusView = this.dumpRadiusView;
104      var itemRadiusWorld = dt.xViewVectorToWorld(height);
105
106      // Draw the memory dumps.
107      var items = this.items_;
108      var loI = tr.b.findLowIndexInSortedArray(
109          items,
110          function(item) { return item.start; },
111          viewLWorld);
112
113      var oldFont = ctx.font;
114      ctx.font = '400 ' + Math.floor(9 * pixelRatio) + 'px Arial';
115      ctx.strokeStyle = 'rgb(0,0,0)';
116      ctx.textBaseline = 'middle';
117      ctx.textAlign = 'center';
118
119      var drawItems = function(selected) {
120        for (var i = loI; i < items.length; ++i) {
121          var item = items[i];
122          var x = item.start;
123          if (x - itemRadiusWorld > viewRWorld)
124            break;
125          if (item.selected !== selected)
126            continue;
127          var xView = dt.xWorldToView(x);
128
129          ctx.fillStyle = EventPresenter.getSelectableItemColorAsString(item);
130          ctx.beginPath();
131          ctx.arc(xView, halfHeight, dumpRadiusView + 0.5, 0, twoPi);
132          ctx.fill();
133          if (item.selected) {
134            ctx.lineWidth = 3;
135            ctx.strokeStyle = 'rgb(100,100,0)';
136            ctx.stroke();
137
138            ctx.beginPath();
139            ctx.arc(xView, halfHeight, dumpRadiusView, 0, twoPi);
140            ctx.lineWidth = 1.5;
141            ctx.strokeStyle = 'rgb(255,255,0)';
142            ctx.stroke();
143          } else {
144            ctx.lineWidth = 1;
145            ctx.strokeStyle = 'rgb(0,0,0)';
146            ctx.stroke();
147          }
148
149          ctx.fillStyle = 'rgb(255, 255, 255)';
150          ctx.fillText(item.dotLetter, xView, halfHeight);
151        }
152      };
153
154      // Draw unselected items first to make sure they don't occlude selected
155      // items.
156      drawItems(false);
157      drawItems(true);
158
159      ctx.lineWidth = 1;
160      ctx.font = oldFont;
161    },
162
163    addEventsToTrackMap: function(eventToTrackMap) {
164      if (this.items_ === undefined)
165        return;
166
167      this.items_.forEach(function(item) {
168        item.addToTrackMap(eventToTrackMap, this);
169      }, this);
170    },
171
172    addIntersectingEventsInRangeToSelectionInWorldSpace: function(
173        loWX, hiWX, viewPixWidthWorld, selection) {
174      if (this.items_ === undefined)
175        return;
176
177      var itemRadiusWorld = viewPixWidthWorld * this.dumpRadiusView;
178      tr.b.iterateOverIntersectingIntervals(
179          this.items_,
180          function(x) { return x.start - itemRadiusWorld; },
181          function(x) { return 2 * itemRadiusWorld; },
182          loWX, hiWX,
183          function(item) {
184            item.addToSelection(selection);
185          }.bind(this));
186    },
187
188    /**
189     * Add the item to the left or right of the provided event, if any, to the
190     * selection.
191     * @param {event} The current event item.
192     * @param {Number} offset Number of slices away from the event to look.
193     * @param {Selection} selection The selection to add an event to,
194     * if found.
195     * @return {boolean} Whether an event was found.
196     * @private
197     */
198    addEventNearToProvidedEventToSelection: function(event, offset, selection) {
199      if (this.items_ === undefined)
200        return;
201
202      var items = this.items_;
203      var index = tr.b.findFirstIndexInArray(items, function(item) {
204        return item.modelItem === event;
205      });
206      if (index === -1)
207        return false;
208
209      var newIndex = index + offset;
210      if (newIndex >= 0 && newIndex < items.length) {
211        items[newIndex].addToSelection(selection);
212        return true;
213      }
214      return false;
215    },
216
217    addAllEventsMatchingFilterToSelection: function(filter, selection) {
218    },
219
220    addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
221                                         selection) {
222      if (this.items_ === undefined)
223        return;
224
225      var item = tr.b.findClosestElementInSortedArray(
226          this.items_,
227          function(x) { return x.start; },
228          worldX,
229          worldMaxDist);
230
231      if (!item)
232        return;
233
234      item.addToSelection(selection);
235    }
236  };
237
238  /**
239   * A filled dot with a letter inside it.
240   *
241   * @constructor
242   * @extends {ProxySelectableItem}
243   */
244  function LetterDot(modelItem, dotLetter, colorId, start) {
245    tr.model.ProxySelectableItem.call(this, modelItem);
246    this.dotLetter = dotLetter;
247    this.colorId = colorId;
248    this.start = start;
249  };
250
251  LetterDot.prototype = {
252    __proto__: tr.model.ProxySelectableItem.prototype
253  };
254
255  return {
256    LetterDotTrack: LetterDotTrack,
257    LetterDot: LetterDot
258  };
259});
260</script>
261