1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/layout/grid_layout.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "base/stl_util.h"
11#include "ui/gfx/insets.h"
12#include "ui/views/layout/layout_constants.h"
13#include "ui/views/view.h"
14#include "ui/views/window/dialog_delegate.h"
15
16namespace views {
17
18// LayoutElement ------------------------------------------------------
19
20// A LayoutElement has a size and location along one axis. It contains
21// methods that are used along both axis.
22class LayoutElement {
23 public:
24  // Invokes ResetSize on all the layout elements.
25  template <class T>
26  static void ResetSizes(std::vector<T*>* elements) {
27    // Reset the layout width of each column.
28    for (typename std::vector<T*>::iterator i = elements->begin();
29         i != elements->end(); ++i) {
30      (*i)->ResetSize();
31    }
32  }
33
34  // Sets the location of each element to be the sum of the sizes of the
35  // preceding elements.
36  template <class T>
37  static void CalculateLocationsFromSize(std::vector<T*>* elements) {
38    // Reset the layout width of each column.
39    int location = 0;
40    for (typename std::vector<T*>::iterator i = elements->begin();
41         i != elements->end(); ++i) {
42      (*i)->SetLocation(location);
43      location += (*i)->Size();
44    }
45  }
46
47  // Distributes delta among the resizable elements.
48  // Each resizable element is given ResizePercent / total_percent * delta
49  // pixels extra of space.
50  template <class T>
51  static void DistributeDelta(int delta, std::vector<T*>* elements) {
52    if (delta == 0)
53      return;
54
55    float total_percent = 0;
56    int resize_count = 0;
57    for (typename std::vector<T*>::iterator i = elements->begin();
58         i != elements->end(); ++i) {
59      total_percent += (*i)->ResizePercent();
60      resize_count++;
61    }
62    if (total_percent == 0) {
63      // None of the elements are resizable, return.
64      return;
65    }
66    int remaining = delta;
67    int resized = resize_count;
68    for (typename std::vector<T*>::iterator i = elements->begin();
69         i != elements->end(); ++i) {
70      T* element = *i;
71      if (element->ResizePercent() > 0) {
72        int to_give;
73        if (--resized == 0) {
74          to_give = remaining;
75        } else {
76          to_give = static_cast<int>(delta *
77                                    (element->resize_percent_ / total_percent));
78          remaining -= to_give;
79        }
80        element->SetSize(element->Size() + to_give);
81      }
82    }
83  }
84
85  // Returns the sum of the size of the elements from start to start + length.
86  template <class T>
87  static int TotalSize(int start, int length, std::vector<T*>* elements) {
88    DCHECK(start >= 0 && length > 0 &&
89           start + length <= static_cast<int>(elements->size()));
90    int size = 0;
91    for (int i = start, max = start + length; i < max; ++i) {
92      size += (*elements)[i]->Size();
93    }
94    return size;
95  }
96
97  explicit LayoutElement(float resize_percent)
98      : resize_percent_(resize_percent) {
99    DCHECK(resize_percent >= 0);
100  }
101
102  virtual ~LayoutElement() {}
103
104  void SetLocation(int location) {
105    location_ = location;
106  }
107
108  int Location() {
109    return location_;
110  }
111
112  // Adjusts the size of this LayoutElement to be the max of the current size
113  // and the specified size.
114  virtual void AdjustSize(int size) {
115    size_ = std::max(size_, size);
116  }
117
118  // Resets the size to the initial size. This sets the size to 0, but
119  // subclasses that have a different initial size should override.
120  virtual void ResetSize() {
121    SetSize(0);
122  }
123
124  void SetSize(int size) {
125    size_ = size;
126  }
127
128  int Size() {
129    return size_;
130  }
131
132  void SetResizePercent(float percent) {
133    resize_percent_ = percent;
134  }
135
136  float ResizePercent() {
137    return resize_percent_;
138  }
139
140  bool IsResizable() {
141    return resize_percent_ > 0;
142  }
143
144 private:
145  float resize_percent_;
146  int location_;
147  int size_;
148
149  DISALLOW_COPY_AND_ASSIGN(LayoutElement);
150};
151
152// Column -------------------------------------------------------------
153
154// As the name implies, this represents a Column. Column contains default
155// values for views originating in this column.
156class Column : public LayoutElement {
157 public:
158  Column(GridLayout::Alignment h_align,
159         GridLayout::Alignment v_align,
160         float resize_percent,
161         GridLayout::SizeType size_type,
162         int fixed_width,
163         int min_width,
164         size_t index,
165         bool is_padding)
166    : LayoutElement(resize_percent),
167      h_align_(h_align),
168      v_align_(v_align),
169      size_type_(size_type),
170      same_size_column_(-1),
171      fixed_width_(fixed_width),
172      min_width_(min_width),
173      index_(index),
174      is_padding_(is_padding),
175      master_column_(NULL) {}
176
177  virtual ~Column() {}
178
179  GridLayout::Alignment h_align() { return h_align_; }
180  GridLayout::Alignment v_align() { return v_align_; }
181
182  virtual void ResetSize() OVERRIDE;
183
184 private:
185  friend class ColumnSet;
186  friend class GridLayout;
187
188  Column* GetLastMasterColumn();
189
190  // Determines the max size of all linked columns, and sets each column
191  // to that size. This should only be used for the master column.
192  void UnifySameSizedColumnSizes();
193
194  virtual void AdjustSize(int size) OVERRIDE;
195
196  const GridLayout::Alignment h_align_;
197  const GridLayout::Alignment v_align_;
198  const GridLayout::SizeType size_type_;
199  int same_size_column_;
200  const int fixed_width_;
201  const int min_width_;
202
203  // Index of this column in the ColumnSet.
204  const size_t index_;
205
206  const bool is_padding_;
207
208  // If multiple columns have their sizes linked, one is the
209  // master column. The master column is identified by the
210  // master_column field being equal to itself. The master columns
211  // same_size_columns field contains the set of Columns with the
212  // the same size. Columns who are linked to other columns, but
213  // are not the master column have their master_column pointing to
214  // one of the other linked columns. Use the method GetLastMasterColumn
215  // to resolve the true master column.
216  std::vector<Column*> same_size_columns_;
217  Column* master_column_;
218
219  DISALLOW_COPY_AND_ASSIGN(Column);
220};
221
222void Column::ResetSize() {
223  if (size_type_ == GridLayout::FIXED) {
224    SetSize(fixed_width_);
225  } else {
226    SetSize(min_width_);
227  }
228}
229
230Column* Column::GetLastMasterColumn() {
231  if (master_column_ == NULL) {
232    return NULL;
233  }
234  if (master_column_ == this) {
235    return this;
236  }
237  return master_column_->GetLastMasterColumn();
238}
239
240void Column::UnifySameSizedColumnSizes() {
241  DCHECK(master_column_ == this);
242
243  // Accumulate the size first.
244  int size = 0;
245  for (std::vector<Column*>::iterator i = same_size_columns_.begin();
246       i != same_size_columns_.end(); ++i) {
247      size = std::max(size, (*i)->Size());
248  }
249
250  // Then apply it.
251  for (std::vector<Column*>::iterator i = same_size_columns_.begin();
252       i != same_size_columns_.end(); ++i) {
253      (*i)->SetSize(size);
254  }
255}
256
257void Column::AdjustSize(int size) {
258  if (size_type_ == GridLayout::USE_PREF)
259    LayoutElement::AdjustSize(size);
260}
261
262// Row -------------------------------------------------------------
263
264class Row : public LayoutElement {
265 public:
266  Row(bool fixed_height, int height, float resize_percent,
267      ColumnSet* column_set)
268    : LayoutElement(resize_percent),
269      fixed_height_(fixed_height),
270      height_(height),
271      column_set_(column_set),
272      max_ascent_(0),
273      max_descent_(0) {
274  }
275
276  virtual ~Row() {}
277
278  virtual void ResetSize() OVERRIDE {
279    max_ascent_ = max_descent_ = 0;
280    SetSize(height_);
281  }
282
283  ColumnSet* column_set() {
284    return column_set_;
285  }
286
287  // Adjusts the size to accomodate the specified ascent/descent.
288  void AdjustSizeForBaseline(int ascent, int descent) {
289    max_ascent_ = std::max(ascent, max_ascent_);
290    max_descent_ = std::max(descent, max_descent_);
291    AdjustSize(max_ascent_ + max_descent_);
292  }
293
294  int max_ascent() const {
295    return max_ascent_;
296  }
297
298  int max_descent() const {
299    return max_descent_;
300  }
301
302 private:
303  const bool fixed_height_;
304  const int height_;
305  // The column set used for this row; null for padding rows.
306  ColumnSet* column_set_;
307
308  int max_ascent_;
309  int max_descent_;
310
311  DISALLOW_COPY_AND_ASSIGN(Row);
312};
313
314// ViewState -------------------------------------------------------------
315
316// Identifies the location in the grid of a particular view, along with
317// placement information and size information.
318struct ViewState {
319  ViewState(ColumnSet* column_set, View* view, int start_col, int start_row,
320            int col_span, int row_span, GridLayout::Alignment h_align,
321            GridLayout::Alignment v_align, int pref_width, int pref_height)
322      : column_set(column_set),
323        view(view),
324        start_col(start_col),
325        start_row(start_row),
326        col_span(col_span),
327        row_span(row_span),
328        h_align(h_align),
329        v_align(v_align),
330        pref_width_fixed(pref_width > 0),
331        pref_height_fixed(pref_height > 0),
332        pref_width(pref_width),
333        pref_height(pref_height),
334        remaining_width(0),
335        remaining_height(0),
336        baseline(-1) {
337    DCHECK(view && start_col >= 0 && start_row >= 0 && col_span > 0 &&
338           row_span > 0 && start_col < column_set->num_columns() &&
339           (start_col + col_span) <= column_set->num_columns());
340  }
341
342  ColumnSet* const column_set;
343  View* const view;
344  const int start_col;
345  const int start_row;
346  const int col_span;
347  const int row_span;
348  const GridLayout::Alignment h_align;
349  const GridLayout::Alignment v_align;
350
351  // If true, the pref_width/pref_height were explicitly set and the view's
352  // preferred size is ignored.
353  const bool pref_width_fixed;
354  const bool pref_height_fixed;
355
356  // The preferred width/height. These are reset during the layout process.
357  int pref_width;
358  int pref_height;
359
360  // Used during layout. Gives how much width/height has not yet been
361  // distributed to the columns/rows the view is in.
362  int remaining_width;
363  int remaining_height;
364
365  // The baseline. Only used if the view is vertically aligned along the
366  // baseline.
367  int baseline;
368};
369
370static bool CompareByColumnSpan(const ViewState* v1, const ViewState* v2) {
371  return v1->col_span < v2->col_span;
372}
373
374static bool CompareByRowSpan(const ViewState* v1, const ViewState* v2) {
375  return v1->row_span < v2->row_span;
376}
377
378// ColumnSet -------------------------------------------------------------
379
380ColumnSet::ColumnSet(int id) : id_(id) {
381}
382
383ColumnSet::~ColumnSet() {
384  STLDeleteElements(&columns_);
385}
386
387void ColumnSet::AddPaddingColumn(float resize_percent, int width) {
388  AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent,
389            GridLayout::FIXED, width, width, true);
390}
391
392void ColumnSet::AddColumn(GridLayout::Alignment h_align,
393                          GridLayout::Alignment v_align,
394                          float resize_percent,
395                          GridLayout::SizeType size_type,
396                          int fixed_width,
397                          int min_width) {
398  AddColumn(h_align, v_align, resize_percent, size_type, fixed_width,
399            min_width, false);
400}
401
402
403void ColumnSet::LinkColumnSizes(int first, ...) {
404  va_list marker;
405  va_start(marker, first);
406  DCHECK(first >= 0 && first < num_columns());
407  for (int last = first, next = va_arg(marker, int); next != -1;
408       next = va_arg(marker, int)) {
409    DCHECK(next >= 0 && next < num_columns());
410    columns_[last]->same_size_column_ = next;
411    last = next;
412  }
413  va_end(marker);
414}
415
416void ColumnSet::AddColumn(GridLayout::Alignment h_align,
417                          GridLayout::Alignment v_align,
418                          float resize_percent,
419                          GridLayout::SizeType size_type,
420                          int fixed_width,
421                          int min_width,
422                          bool is_padding) {
423  Column* column = new Column(h_align, v_align, resize_percent, size_type,
424                              fixed_width, min_width, columns_.size(),
425                              is_padding);
426  columns_.push_back(column);
427}
428
429void ColumnSet::AddViewState(ViewState* view_state) {
430  // view_states are ordered by column_span (in ascending order).
431  std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(),
432                                                    view_states_.end(),
433                                                    view_state,
434                                                    CompareByColumnSpan);
435  view_states_.insert(i, view_state);
436}
437
438void ColumnSet::CalculateMasterColumns() {
439  for (std::vector<Column*>::iterator i = columns_.begin();
440       i != columns_.end(); ++i) {
441    Column* column = *i;
442    int same_size_column_index = column->same_size_column_;
443    if (same_size_column_index != -1) {
444      DCHECK(same_size_column_index >= 0 &&
445             same_size_column_index < static_cast<int>(columns_.size()));
446      Column* master_column = column->master_column_;
447      Column* same_size_column = columns_[same_size_column_index];
448      Column* same_size_column_master = same_size_column->master_column_;
449      if (master_column == NULL) {
450        // Current column is not linked to any other column.
451        if (same_size_column_master == NULL) {
452          // Both columns are not linked.
453          column->master_column_ = column;
454          same_size_column->master_column_ = column;
455          column->same_size_columns_.push_back(same_size_column);
456          column->same_size_columns_.push_back(column);
457        } else {
458          // Column to link to is linked with other columns.
459          // Add current column to list of linked columns in other columns
460          // master column.
461          same_size_column->GetLastMasterColumn()->
462              same_size_columns_.push_back(column);
463          // And update the master column for the current column to that
464          // of the same sized column.
465          column->master_column_ = same_size_column;
466        }
467      } else {
468        // Current column is already linked with another column.
469        if (same_size_column_master == NULL) {
470          // Column to link with is not linked to any other columns.
471          // Update it's master_column.
472          same_size_column->master_column_ = column;
473          // Add linked column to list of linked column.
474          column->GetLastMasterColumn()->same_size_columns_.
475              push_back(same_size_column);
476        } else if (column->GetLastMasterColumn() !=
477                   same_size_column->GetLastMasterColumn()) {
478          // The two columns are already linked with other columns.
479          std::vector<Column*>* same_size_columns =
480              &(column->GetLastMasterColumn()->same_size_columns_);
481          std::vector<Column*>* other_same_size_columns =
482              &(same_size_column->GetLastMasterColumn()->same_size_columns_);
483          // Add all the columns from the others master to current columns
484          // master.
485          same_size_columns->insert(same_size_columns->end(),
486                                     other_same_size_columns->begin(),
487                                     other_same_size_columns->end());
488          // The other master is no longer a master, clear its vector of
489          // linked columns, and reset its master_column.
490          other_same_size_columns->clear();
491          same_size_column->GetLastMasterColumn()->master_column_ = column;
492        }
493      }
494    }
495  }
496  AccumulateMasterColumns();
497}
498
499void ColumnSet::AccumulateMasterColumns() {
500  DCHECK(master_columns_.empty());
501  for (std::vector<Column*>::iterator i = columns_.begin();
502       i != columns_.end(); ++i) {
503    Column* column = *i;
504    Column* master_column = column->GetLastMasterColumn();
505    if (master_column &&
506        find(master_columns_.begin(), master_columns_.end(),
507             master_column) == master_columns_.end()) {
508      master_columns_.push_back(master_column);
509    }
510    // At this point, GetLastMasterColumn may not == master_column
511    // (may have to go through a few Columns)_. Reset master_column to
512    // avoid hops.
513    column->master_column_ = master_column;
514  }
515}
516
517void ColumnSet::UnifySameSizedColumnSizes() {
518  for (std::vector<Column*>::iterator i = master_columns_.begin();
519       i != master_columns_.end(); ++i) {
520    (*i)->UnifySameSizedColumnSizes();
521  }
522}
523
524void ColumnSet::UpdateRemainingWidth(ViewState* view_state) {
525  for (int i = view_state->start_col,
526       max_col = view_state->start_col + view_state->col_span;
527       i < max_col; ++i) {
528    view_state->remaining_width -= columns_[i]->Size();
529  }
530}
531
532void ColumnSet::DistributeRemainingWidth(ViewState* view_state) {
533  // This is nearly the same as that for rows, but differs in so far as how
534  // Rows and Columns are treated. Rows have two states, resizable or not.
535  // Columns have three, resizable, USE_PREF or not resizable. This results
536  // in slightly different handling for distributing unaccounted size.
537  int width = view_state->remaining_width;
538  if (width <= 0) {
539    // The columns this view is in are big enough to accommodate it.
540    return;
541  }
542
543  // Determine which columns are resizable, and which have a size type
544  // of USE_PREF.
545  int resizable_columns = 0;
546  int pref_size_columns = 0;
547  int start_col = view_state->start_col;
548  int max_col = view_state->start_col + view_state->col_span;
549  float total_resize = 0;
550  for (int i = start_col; i < max_col; ++i) {
551    if (columns_[i]->IsResizable()) {
552      total_resize += columns_[i]->ResizePercent();
553      resizable_columns++;
554    } else if (columns_[i]->size_type_ == GridLayout::USE_PREF) {
555      pref_size_columns++;
556    }
557  }
558
559  if (resizable_columns > 0) {
560    // There are resizable columns, give them the remaining width. The extra
561    // width is distributed using the resize values of each column.
562    int remaining_width = width;
563    for (int i = start_col, resize_i = 0; i < max_col; ++i) {
564      if (columns_[i]->IsResizable()) {
565        resize_i++;
566        int delta = (resize_i == resizable_columns) ? remaining_width :
567            static_cast<int>(width * columns_[i]->ResizePercent() /
568                             total_resize);
569        remaining_width -= delta;
570        columns_[i]->SetSize(columns_[i]->Size() + delta);
571      }
572    }
573  } else if (pref_size_columns > 0) {
574    // None of the columns are resizable, distribute the width among those
575    // that use the preferred size.
576    int to_distribute = width / pref_size_columns;
577    for (int i = start_col; i < max_col; ++i) {
578      if (columns_[i]->size_type_ == GridLayout::USE_PREF) {
579        width -= to_distribute;
580        if (width < to_distribute)
581          to_distribute += width;
582        columns_[i]->SetSize(columns_[i]->Size() + to_distribute);
583      }
584    }
585  }
586}
587
588int ColumnSet::LayoutWidth() {
589  int width = 0;
590  for (std::vector<Column*>::iterator i = columns_.begin();
591       i != columns_.end(); ++i) {
592    width += (*i)->Size();
593  }
594  return width;
595}
596
597int ColumnSet::GetColumnWidth(int start_col, int col_span) {
598  return LayoutElement::TotalSize(start_col, col_span, &columns_);
599}
600
601void ColumnSet::ResetColumnXCoordinates() {
602  LayoutElement::CalculateLocationsFromSize(&columns_);
603}
604
605void ColumnSet::CalculateSize() {
606  gfx::Size pref;
607  // Reset the preferred and remaining sizes.
608  for (std::vector<ViewState*>::iterator i = view_states_.begin();
609       i != view_states_.end(); ++i) {
610    ViewState* view_state = *i;
611    if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) {
612      pref = view_state->view->GetPreferredSize();
613      if (!view_state->pref_width_fixed)
614        view_state->pref_width = pref.width();
615      if (!view_state->pref_height_fixed)
616        view_state->pref_height = pref.height();
617    }
618    view_state->remaining_width = pref.width();
619    view_state->remaining_height = pref.height();
620  }
621
622  // Let layout element reset the sizes for us.
623  LayoutElement::ResetSizes(&columns_);
624
625  // Distribute the size of each view with a col span == 1.
626  std::vector<ViewState*>::iterator view_state_iterator =
627      view_states_.begin();
628  for (; view_state_iterator != view_states_.end() &&
629         (*view_state_iterator)->col_span == 1; ++view_state_iterator) {
630    ViewState* view_state = *view_state_iterator;
631    Column* column = columns_[view_state->start_col];
632    column->AdjustSize(view_state->pref_width);
633    view_state->remaining_width -= column->Size();
634  }
635
636  // Make sure all linked columns have the same size.
637  UnifySameSizedColumnSizes();
638
639  // Distribute the size of each view with a column span > 1.
640  for (; view_state_iterator != view_states_.end(); ++view_state_iterator) {
641    ViewState* view_state = *view_state_iterator;
642
643    // Update the remaining_width from columns this view_state touches.
644    UpdateRemainingWidth(view_state);
645
646    // Distribute the remaining width.
647    DistributeRemainingWidth(view_state);
648
649    // Update the size of linked columns.
650    // This may need to be combined with previous step.
651    UnifySameSizedColumnSizes();
652  }
653}
654
655void ColumnSet::Resize(int delta) {
656  LayoutElement::DistributeDelta(delta, &columns_);
657}
658
659// GridLayout -------------------------------------------------------------
660
661GridLayout::GridLayout(View* host)
662    : host_(host),
663      calculated_master_columns_(false),
664      remaining_row_span_(0),
665      current_row_(-1),
666      next_column_(0),
667      current_row_col_set_(NULL),
668      adding_view_(false) {
669  DCHECK(host);
670}
671
672GridLayout::~GridLayout() {
673  STLDeleteElements(&column_sets_);
674  STLDeleteElements(&view_states_);
675  STLDeleteElements(&rows_);
676}
677
678// static
679GridLayout* GridLayout::CreatePanel(View* host) {
680  GridLayout* layout = new GridLayout(host);
681  layout->SetInsets(kPanelVertMargin, kButtonHEdgeMarginNew,
682                    kPanelVertMargin, kButtonHEdgeMarginNew);
683  return layout;
684}
685
686void GridLayout::SetInsets(int top, int left, int bottom, int right) {
687  insets_.Set(top, left, bottom, right);
688}
689
690void GridLayout::SetInsets(const gfx::Insets& insets) {
691  insets_ = insets;
692}
693
694ColumnSet* GridLayout::AddColumnSet(int id) {
695  DCHECK(GetColumnSet(id) == NULL);
696  ColumnSet* column_set = new ColumnSet(id);
697  column_sets_.push_back(column_set);
698  return column_set;
699}
700
701ColumnSet* GridLayout::GetColumnSet(int id) {
702  for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
703       i != column_sets_.end(); ++i) {
704    if ((*i)->id_ == id) {
705      return *i;
706    }
707  }
708  return NULL;
709}
710
711void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id,
712                                     float padding_resize, int padding) {
713  AddPaddingRow(padding_resize, padding);
714  StartRow(vertical_resize, column_set_id);
715}
716
717void GridLayout::StartRow(float vertical_resize, int column_set_id) {
718  ColumnSet* column_set = GetColumnSet(column_set_id);
719  DCHECK(column_set);
720  AddRow(new Row(false, 0, vertical_resize, column_set));
721}
722
723void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) {
724  AddRow(new Row(true, pixel_count, vertical_resize, NULL));
725}
726
727void GridLayout::SkipColumns(int col_count) {
728  DCHECK(col_count > 0);
729  next_column_ += col_count;
730  DCHECK(current_row_col_set_ &&
731         next_column_ <= current_row_col_set_->num_columns());
732  SkipPaddingColumns();
733}
734
735void GridLayout::AddView(View* view) {
736  AddView(view, 1, 1);
737}
738
739void GridLayout::AddView(View* view, int col_span, int row_span) {
740  DCHECK(current_row_col_set_ &&
741         next_column_ < current_row_col_set_->num_columns());
742  Column* column = current_row_col_set_->columns_[next_column_];
743  AddView(view, col_span, row_span, column->h_align(), column->v_align());
744}
745
746void GridLayout::AddView(View* view, int col_span, int row_span,
747                         Alignment h_align, Alignment v_align) {
748  AddView(view, col_span, row_span, h_align, v_align, 0, 0);
749}
750
751void GridLayout::AddView(View* view, int col_span, int row_span,
752                         Alignment h_align, Alignment v_align,
753                         int pref_width, int pref_height) {
754  DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
755         (next_column_ + col_span) <= current_row_col_set_->num_columns());
756  // We don't support baseline alignment of views spanning rows. Please add if
757  // you need it.
758  DCHECK(v_align != BASELINE || row_span == 1);
759  ViewState* state =
760      new ViewState(current_row_col_set_, view, next_column_, current_row_,
761                    col_span, row_span, h_align, v_align, pref_width,
762                    pref_height);
763  AddViewState(state);
764}
765
766static void CalculateSize(int pref_size, GridLayout::Alignment alignment,
767                          int* location, int* size) {
768  if (alignment != GridLayout::FILL) {
769    int available_size = *size;
770    *size = std::min(*size, pref_size);
771    switch (alignment) {
772      case GridLayout::LEADING:
773        // Nothing to do, location already points to start.
774        break;
775      case GridLayout::BASELINE:  // If we were asked to align on baseline, but
776                                  // the view doesn't have a baseline, fall back
777                                  // to center.
778      case GridLayout::CENTER:
779        *location += (available_size - *size) / 2;
780        break;
781      case GridLayout::TRAILING:
782        *location = *location + available_size - *size;
783        break;
784      default:
785        NOTREACHED();
786    }
787  }
788}
789
790void GridLayout::Installed(View* host) {
791  DCHECK(host_ == host);
792}
793
794void GridLayout::Uninstalled(View* host) {
795  DCHECK(host_ == host);
796}
797
798void GridLayout::ViewAdded(View* host, View* view) {
799  DCHECK(host_ == host && adding_view_);
800}
801
802void GridLayout::ViewRemoved(View* host, View* view) {
803  DCHECK(host_ == host);
804}
805
806void GridLayout::Layout(View* host) {
807  DCHECK(host_ == host);
808  // SizeRowsAndColumns sets the size and location of each row/column, but
809  // not of the views.
810  gfx::Size pref;
811  SizeRowsAndColumns(true, host_->width(), host_->height(), &pref);
812
813  // Size each view.
814  for (std::vector<ViewState*>::iterator i = view_states_.begin();
815       i != view_states_.end(); ++i) {
816    ViewState* view_state = *i;
817    ColumnSet* column_set = view_state->column_set;
818    View* view = (*i)->view;
819    DCHECK(view);
820    int x = column_set->columns_[view_state->start_col]->Location() +
821            insets_.left();
822    int width = column_set->GetColumnWidth(view_state->start_col,
823                                           view_state->col_span);
824    CalculateSize(view_state->pref_width, view_state->h_align,
825                  &x, &width);
826    int y = rows_[view_state->start_row]->Location() + insets_.top();
827    int height = LayoutElement::TotalSize(view_state->start_row,
828                                          view_state->row_span, &rows_);
829    if (view_state->v_align == BASELINE && view_state->baseline != -1) {
830      y += rows_[view_state->start_row]->max_ascent() - view_state->baseline;
831      height = view_state->pref_height;
832    } else {
833      CalculateSize(view_state->pref_height, view_state->v_align, &y, &height);
834    }
835    view->SetBounds(x, y, width, height);
836  }
837}
838
839gfx::Size GridLayout::GetPreferredSize(View* host) {
840  DCHECK(host_ == host);
841  gfx::Size out;
842  SizeRowsAndColumns(false, 0, 0, &out);
843  out.SetSize(std::max(out.width(), minimum_size_.width()),
844              std::max(out.height(), minimum_size_.height()));
845  return out;
846}
847
848int GridLayout::GetPreferredHeightForWidth(View* host, int width) {
849  DCHECK(host_ == host);
850  gfx::Size pref;
851  SizeRowsAndColumns(false, width, 0, &pref);
852  return pref.height();
853}
854
855void GridLayout::SizeRowsAndColumns(bool layout, int width, int height,
856                                    gfx::Size* pref) {
857  // Make sure the master columns have been calculated.
858  CalculateMasterColumnsIfNecessary();
859  pref->SetSize(0, 0);
860  if (rows_.empty())
861    return;
862
863  // Calculate the preferred width of each of the columns. Some views'
864  // preferred heights are derived from their width, as such we need to
865  // calculate the size of the columns first.
866  for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
867       i != column_sets_.end(); ++i) {
868    (*i)->CalculateSize();
869    pref->set_width(std::max(pref->width(), (*i)->LayoutWidth()));
870  }
871  pref->set_width(pref->width() + insets_.width());
872
873  // Go over the columns again and set them all to the size we settled for.
874  width = width ? width : pref->width();
875  for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
876       i != column_sets_.end(); ++i) {
877    // We're doing a layout, divy up any extra space.
878    (*i)->Resize(width - (*i)->LayoutWidth() - insets_.left() -
879                 insets_.right());
880    // And reset the x coordinates.
881    (*i)->ResetColumnXCoordinates();
882  }
883
884  // Reset the height of each row.
885  LayoutElement::ResetSizes(&rows_);
886
887  // Do the following:
888  // . If the view is aligned along it's baseline, obtain the baseline from the
889  //   view and update the rows ascent/descent.
890  // . Reset the remaining_height of each view state.
891  // . If the width the view will be given is different than it's pref, ask
892  //   for the height given a particularly width.
893  for (std::vector<ViewState*>::iterator i= view_states_.begin();
894       i != view_states_.end() ; ++i) {
895    ViewState* view_state = *i;
896    view_state->remaining_height = view_state->pref_height;
897
898    if (view_state->v_align == BASELINE)
899      view_state->baseline = view_state->view->GetBaseline();
900
901    if (view_state->h_align == FILL) {
902      // The view is resizable. As the pref height may vary with the width,
903      // ask for the pref again.
904      int actual_width =
905          view_state->column_set->GetColumnWidth(view_state->start_col,
906                                                 view_state->col_span);
907      if (actual_width != view_state->pref_width &&
908          !view_state->pref_height_fixed) {
909        // The width this view will get differs from its preferred. Some Views
910        // pref height varies with its width; ask for the preferred again.
911        view_state->pref_height =
912            view_state->view->GetHeightForWidth(actual_width);
913        view_state->remaining_height = view_state->pref_height;
914      }
915    }
916  }
917
918  // Update the height/ascent/descent of each row from the views.
919  std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin();
920  for (; view_states_iterator != view_states_.end() &&
921      (*view_states_iterator)->row_span == 1; ++view_states_iterator) {
922    ViewState* view_state = *view_states_iterator;
923    Row* row = rows_[view_state->start_row];
924    row->AdjustSize(view_state->remaining_height);
925    if (view_state->baseline != -1 &&
926        view_state->baseline <= view_state->pref_height) {
927      row->AdjustSizeForBaseline(view_state->baseline,
928          view_state->pref_height - view_state->baseline);
929    }
930    view_state->remaining_height = 0;
931  }
932
933  // Distribute the height of each view with a row span > 1.
934  for (; view_states_iterator != view_states_.end(); ++view_states_iterator) {
935    ViewState* view_state = *view_states_iterator;
936
937    // Update the remaining_width from columns this view_state touches.
938    UpdateRemainingHeightFromRows(view_state);
939
940    // Distribute the remaining height.
941    DistributeRemainingHeight(view_state);
942  }
943
944  // Update the location of each of the rows.
945  LayoutElement::CalculateLocationsFromSize(&rows_);
946
947  // We now know the preferred height, set it here.
948  pref->set_height(rows_[rows_.size() - 1]->Location() +
949      rows_[rows_.size() - 1]->Size() + insets_.height());
950
951  if (layout && height != pref->height()) {
952    // We're doing a layout, and the height differs from the preferred height,
953    // divy up the extra space.
954    LayoutElement::DistributeDelta(height - pref->height(), &rows_);
955
956    // Reset y locations.
957    LayoutElement::CalculateLocationsFromSize(&rows_);
958  }
959}
960
961void GridLayout::CalculateMasterColumnsIfNecessary() {
962  if (!calculated_master_columns_) {
963    calculated_master_columns_ = true;
964    for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
965         i != column_sets_.end(); ++i) {
966      (*i)->CalculateMasterColumns();
967    }
968  }
969}
970
971void GridLayout::AddViewState(ViewState* view_state) {
972  DCHECK(view_state->view && (view_state->view->parent() == NULL ||
973                              view_state->view->parent() == host_));
974  if (!view_state->view->parent()) {
975    adding_view_ = true;
976    host_->AddChildView(view_state->view);
977    adding_view_ = false;
978  }
979  remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span);
980  next_column_ += view_state->col_span;
981  current_row_col_set_->AddViewState(view_state);
982  // view_states are ordered by row_span (in ascending order).
983  std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(),
984                                                    view_states_.end(),
985                                                    view_state,
986                                                    CompareByRowSpan);
987  view_states_.insert(i, view_state);
988  SkipPaddingColumns();
989}
990
991void GridLayout::AddRow(Row* row) {
992  current_row_++;
993  remaining_row_span_--;
994  // GridLayout requires that if you add a View with a row span you use the same
995  // column set for each of the rows the view lands it. This DCHECK verifies
996  // that.
997  DCHECK(remaining_row_span_ <= 0 ||
998         row->column_set() == NULL ||
999         row->column_set() == GetLastValidColumnSet());
1000  next_column_ = 0;
1001  rows_.push_back(row);
1002  current_row_col_set_ = row->column_set();
1003  SkipPaddingColumns();
1004}
1005
1006void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) {
1007  for (int i = 0, start_row = view_state->start_row;
1008       i < view_state->row_span; ++i) {
1009    view_state->remaining_height -= rows_[i + start_row]->Size();
1010  }
1011}
1012
1013void GridLayout::DistributeRemainingHeight(ViewState* view_state) {
1014  int height = view_state->remaining_height;
1015  if (height <= 0)
1016    return;
1017
1018  // Determine the number of resizable rows the view touches.
1019  int resizable_rows = 0;
1020  int start_row = view_state->start_row;
1021  int max_row = view_state->start_row + view_state->row_span;
1022  for (int i = start_row; i < max_row; ++i) {
1023    if (rows_[i]->IsResizable()) {
1024      resizable_rows++;
1025    }
1026  }
1027
1028  if (resizable_rows > 0) {
1029    // There are resizable rows, give the remaining height to them.
1030    int to_distribute = height / resizable_rows;
1031    for (int i = start_row; i < max_row; ++i) {
1032      if (rows_[i]->IsResizable()) {
1033        height -= to_distribute;
1034        if (height < to_distribute) {
1035          // Give all slop to the last column.
1036          to_distribute += height;
1037        }
1038        rows_[i]->SetSize(rows_[i]->Size() + to_distribute);
1039      }
1040    }
1041  } else {
1042    // None of the rows are resizable, divy the remaining height up equally
1043    // among all rows the view touches.
1044    int each_row_height = height / view_state->row_span;
1045    for (int i = start_row; i < max_row; ++i) {
1046      height -= each_row_height;
1047      if (height < each_row_height)
1048        each_row_height += height;
1049      rows_[i]->SetSize(rows_[i]->Size() + each_row_height);
1050    }
1051    view_state->remaining_height = 0;
1052  }
1053}
1054
1055void GridLayout::SkipPaddingColumns() {
1056  if (!current_row_col_set_)
1057    return;
1058  while (next_column_ < current_row_col_set_->num_columns() &&
1059         current_row_col_set_->columns_[next_column_]->is_padding_) {
1060    next_column_++;
1061  }
1062}
1063
1064ColumnSet* GridLayout::GetLastValidColumnSet() {
1065  for (int i = current_row_ - 1; i >= 0; --i) {
1066    if (rows_[i]->column_set())
1067      return rows_[i]->column_set();
1068  }
1069  return NULL;
1070}
1071
1072}  // namespace views
1073