1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 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/ui/tracks/container_track.html">
9<link rel="import" href="/tracing/base/sorted_array_utils.html">
10<link rel="import" href="/tracing/model/model_settings.html">
11<link rel="import" href="/tracing/ui/base/ui.html">
12
13<script>
14'use strict';
15
16tr.exportTo('tr.ui.tracks', function() {
17  /**
18   * A track that displays a group of objects in multiple rows.
19   * @constructor
20   * @extends {ContainerTrack}
21   */
22  var MultiRowTrack = tr.ui.b.define(
23      'multi-row-track', tr.ui.tracks.ContainerTrack);
24
25  MultiRowTrack.prototype = {
26
27    __proto__: tr.ui.tracks.ContainerTrack.prototype,
28
29    decorate: function(viewport) {
30      tr.ui.tracks.ContainerTrack.prototype.decorate.call(this, viewport);
31      this.tooltip_ = '';
32      this.heading_ = '';
33
34      this.groupingSource_ = undefined;
35      this.itemsToGroup_ = undefined;
36
37      this.defaultToCollapsedWhenSubRowCountMoreThan = 1;
38
39      this.itemsGroupedOnLastUpdateContents_ = undefined;
40
41      this.currentSubRows_ = [];
42      this.expanded_ = true;
43    },
44
45    get itemsToGroup() {
46      return this.itemsToGroup_;
47    },
48
49    setItemsToGroup: function(itemsToGroup, opt_groupingSource) {
50      this.itemsToGroup_ = itemsToGroup;
51      this.groupingSource_ = opt_groupingSource;
52      this.updateContents_();
53      this.updateExpandedStateFromGroupingSource_();
54    },
55
56    get heading() {
57      return this.heading_;
58    },
59
60    set heading(h) {
61      this.heading_ = h;
62      this.updateContents_();
63    },
64
65    get tooltip() {
66      return this.tooltip_;
67    },
68
69    set tooltip(t) {
70      this.tooltip_ = t;
71      this.updateContents_();
72    },
73
74    get subRows() {
75      return this.currentSubRows_;
76    },
77
78    get hasVisibleContent() {
79      return this.children.length > 0;
80    },
81
82    get expanded() {
83      return this.expanded_;
84    },
85
86    set expanded(expanded) {
87      if (this.expanded_ == expanded)
88        return;
89      this.expanded_ = expanded;
90      this.expandedStateChanged_();
91    },
92
93    onHeadingClicked_: function(e) {
94      if (this.subRows.length <= 1)
95        return;
96      this.expanded = !this.expanded;
97
98      if (this.groupingSource_) {
99        var modelSettings = new tr.model.ModelSettings(
100            this.groupingSource_.model);
101        modelSettings.setSettingFor(this.groupingSource_, 'expanded',
102                                    this.expanded);
103      }
104
105      e.stopPropagation();
106    },
107
108    updateExpandedStateFromGroupingSource_: function() {
109      if (this.groupingSource_) {
110        var numSubRows = this.subRows.length;
111        var modelSettings = new tr.model.ModelSettings(
112            this.groupingSource_.model);
113        if (numSubRows > 1) {
114          var defaultExpanded;
115          if (numSubRows > this.defaultToCollapsedWhenSubRowCountMoreThan) {
116            defaultExpanded = false;
117          } else {
118            defaultExpanded = true;
119          }
120          this.expanded = modelSettings.getSettingFor(
121              this.groupingSource_, 'expanded', defaultExpanded);
122        } else {
123          this.expanded = undefined;
124        }
125      }
126    },
127
128    expandedStateChanged_: function() {
129      var minH = Math.max(2, Math.ceil(18 / this.children.length));
130      var h = (this.expanded_ ? 18 : minH) + 'px';
131
132      for (var i = 0; i < this.children.length; i++) {
133        this.children[i].height = h;
134        if (i === 0)
135          this.children[i].arrowVisible = true;
136        this.children[i].expanded = this.expanded;
137      }
138
139      if (this.children.length === 1) {
140        this.children[0].expanded = true;
141        this.children[0].arrowVisible = false;
142      }
143    },
144
145    updateContents_: function() {
146      tr.ui.tracks.ContainerTrack.prototype.updateContents_.call(this);
147      if (!this.itemsToGroup_) {
148        this.updateHeadingAndTooltip_();
149        this.currentSubRows_ = [];
150        return;
151      }
152
153      if (this.areArrayContentsSame_(this.itemsGroupedOnLastUpdateContents_,
154                                     this.itemsToGroup_)) {
155        this.updateHeadingAndTooltip_();
156        return;
157      }
158
159      this.itemsGroupedOnLastUpdateContents_ = this.itemsToGroup_;
160
161      this.detach();
162      if (!this.itemsToGroup_.length) {
163        this.currentSubRows_ = [];
164        return;
165      }
166      var subRows = this.buildSubRows_(this.itemsToGroup_);
167      this.currentSubRows_ = subRows;
168      for (var srI = 0; srI < subRows.length; srI++) {
169        var subRow = subRows[srI];
170        if (!subRow.length)
171          continue;
172        var track = this.addSubTrack_(subRow);
173        track.addEventListener(
174          'heading-clicked', this.onHeadingClicked_.bind(this));
175      }
176      this.updateHeadingAndTooltip_();
177      this.expandedStateChanged_();
178    },
179
180    updateHeadingAndTooltip_: function() {
181      if (!this.firstChild)
182        return;
183      this.firstChild.heading = this.heading_;
184      this.firstChild.tooltip = this.tooltip_;
185    },
186
187    /**
188     * Breaks up the list of slices into N rows, each of which is a list of
189     * slices that are non overlapping.
190     */
191    buildSubRows_: function(itemsToGroup) {
192      throw new Error('Not implemented');
193    },
194
195    addSubTrack_: function(subRowItems) {
196      throw new Error('Not implemented');
197    },
198
199    areArrayContentsSame_: function(a, b) {
200      if (!a || !b)
201        return false;
202      if (!a.length || !b.length)
203        return false;
204      if (a.length != b.length)
205        return false;
206      for (var i = 0; i < a.length; ++i) {
207        if (a[i] != b[i])
208          return false;
209      }
210      return true;
211    }
212  };
213
214  return {
215    MultiRowTrack: MultiRowTrack
216  };
217});
218</script>
219