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