summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/info_bar_item_view.cc
blob: 6fc6242d16f0cd7978f7ed0f159d0a02273f2265 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "chrome/app/theme/theme_resources.h"
#include "chrome/browser/standard_layout.h"
#include "chrome/browser/views/info_bar_item_view.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/views/external_focus_tracker.h"
#include "chrome/views/image_view.h"
#include "chrome/views/root_view.h"
#include "chrome/views/view_container.h"

#include "generated_resources.h"

namespace {

class HorizontalSpacer : public ChromeViews::View {
 public:
  explicit HorizontalSpacer(int width) : width_(width) {}

  void GetPreferredSize(CSize* out) {
    out->cx = width_;
    out->cy = 0;
  }

 private:
  int width_;
};

const int kInfoBarVerticalSpacing = 3;
const int kInfoBarLeftMargin = 3;
const double kInfoBarHeight = 37.0;

} // namespace

InfoBarItemView::InfoBarItemView()
    : insert_index_(0),
      close_button_(NULL),
      icon_(NULL) {
  Init();
}

InfoBarItemView::~InfoBarItemView() {
}

// static
int InfoBarItemView::CenterPosition(int size, int target_size) {
  return (target_size - size) / 2;
}

void InfoBarItemView::GetPreferredSize(CSize* out) {
  out->cx = GetParent()->GetWidth();
  out->cy = static_cast<int>(kInfoBarHeight * animation_->GetCurrentValue());
}

// The following is an overall note on the underlying implementation. You don't
// need this in order to use this view. Ignore unless you're editing
// implementation:
// Layout() lays out all of its child views, but it uses insert_index_ to
// decide whether to lay out on the left or right. Whenever a view is added or
// removed the insert_index_ is updated accordingly to make sure it is directly
// between left aligned views and right aligned views. Whenever a view is added,
// a spacer view provides padding to the right of the view if the view is
// left aligned, or to the left of the view if the view is right aligned.
// Removing assumes this spacer view exists.
//
// For example, below M stands for built in margins, I stands for the icon
// which is optional and includes padding of its own. L stands for a left
// aligned view, and R for a right aligned view. P is padding, which can be
// zero. The insert index is currently 4, separating the right of left views.
// The numbers represent what index the child views P, R, and L occupy.
//
// M I L P L P P R P R M
//     0 1 2 3 ^4 5 6 7
// Say we call AddChildViewTrailing(right_view, 10). We end up with:
// M I L P L P P R P R P R M
//     0 1 2 3 ^4 5 6 7 8 9
// First the right view was added, then its padding was added, the insert index
// did not need to change because it still separates the right and left views.
// Note that the padding showed up at the lower index, or to the left of the
// right aligned view.
// Then we call AddChildViewLeading(left_view, 0). We end up with:
// M I L P L P L P P R P R P R M
//     0 1 2 3 4 5 ^6 7 8 9 10 11
// First the left view was added, then the insert_index_ was incremented, then
// the padding is added, even though it is zero (It has no effect on layout)
// and insert_index_ is incremented again to keep it between the right and
// left views. Note in this case, the padding appears to the right of the view
// left aligned view. Removing works the same, but in reverse.
void InfoBarItemView::Layout() {
  const int width = GetWidth();
  const int height = GetHeight();

  int next_x = width - kButtonHEdgeMargin;
  int height_diff = static_cast<int>(kInfoBarHeight) - height;
  const int child_count = GetChildViewCount();
  // Anything greater than or equal to insert_index_ is laid out on the right,
  // with the greatest index (the first one added to the right) being laid out
  // rightmost.
  for (int i = child_count - 1; i >= insert_index_ ; i--) {
    View* v = GetChildViewAt(i);
    if (v->IsVisible()) {
      CSize view_size;
      v->GetPreferredSize(&view_size);
      next_x = next_x - view_size.cx;
      v->SetBounds(next_x,
                   CenterPosition(view_size.cy,
                       static_cast<int>(kInfoBarHeight)) - height_diff,
                   view_size.cx,
                   view_size.cy);
    }
  }
  int left_most_x = next_x;

  next_x = kInfoBarLeftMargin;

  // Anything less than insert_index_ is laid out on the left, with the
  // smallest index (the first one added to the left) being laid out leftmost.
  for (int i = 0; i < insert_index_ ; i++) {
    View* v = GetChildViewAt(i);
    if (v->IsVisible()) {
      CSize view_size;
      v->GetPreferredSize(&view_size);
      int remaining_space = std::max(0, left_most_x - next_x);
      if (view_size.cx > remaining_space) {
        view_size.cx = remaining_space;
      }
      v->SetBounds(next_x,
                   CenterPosition(view_size.cy,
                       static_cast<int>(kInfoBarHeight)) - height_diff,
                   view_size.cx,
                   view_size.cy);
      next_x = next_x + view_size.cx;
    }
  }
}

void InfoBarItemView::DidChangeBounds(const CRect& previous,
                                      const CRect& current) {
  if (GetParent() != NULL)
    Layout();
}

void InfoBarItemView::BeginClose() {
  animation_->Hide();
}

void InfoBarItemView::Close() {
  ChromeViews::View* parent = GetParent();
  parent->RemoveChildView(this);
  if (focus_tracker_.get() != NULL)
    focus_tracker_->FocusLastFocusedExternalView();
  delete this;
}

void InfoBarItemView::CloseButtonPressed() {
  // Close this view by default.
  BeginClose();
}

void InfoBarItemView::AddChildViewTrailing(ChromeViews::View* view,
                                          int leading_padding) {
  ChromeViews::View::AddChildView(insert_index_, view);
  View* padding = new HorizontalSpacer(leading_padding);
  ChromeViews::View::AddChildView(insert_index_, padding);
}

void InfoBarItemView::AddChildViewTrailing(ChromeViews::View* view) {
  AddChildViewTrailing(view, kUnrelatedControlHorizontalSpacing);
}

void InfoBarItemView::AddChildViewLeading(ChromeViews::View* view,
                                         int trailing_padding) {
  ChromeViews::View::AddChildView(insert_index_, view);
  insert_index_++;
  View* padding = new HorizontalSpacer(trailing_padding);
  ChromeViews::View::AddChildView(insert_index_, padding);
  insert_index_++;
}

void InfoBarItemView::AddChildViewLeading(ChromeViews::View* view) {
  AddChildViewLeading(view, kRelatedControlSmallHorizontalSpacing);
}

void InfoBarItemView::SetIcon(const SkBitmap& icon) {
  if (icon_ == NULL) {
    // Add the icon and its padding to the far left of the info bar, and adjust
    // the insert index accordingly.
    icon_ = new ChromeViews::ImageView();
    View* padding = new HorizontalSpacer(kRelatedControlHorizontalSpacing);
    ChromeViews::View::AddChildView(0, padding);
    ChromeViews::View::AddChildView(0, icon_);
    insert_index_ += 2;
  }
  icon_->SetImage(icon);
  Layout();
}

void InfoBarItemView::ViewHierarchyChanged(bool is_add,
                                           View *parent,
                                           View *child) {
  if (child == this) {
    if (is_add) {
      Layout();

      View* root_view = GetRootView();
      HWND root_hwnd = NULL;
      if (root_view)
        root_hwnd = root_view->GetViewContainer()->GetHWND();

      if (root_hwnd) {
        focus_tracker_.reset(new ChromeViews::ExternalFocusTracker(
            this, ChromeViews::FocusManager::GetFocusManager(root_hwnd)));
      }
    } else {
      // When we're removed from the hierarchy our focus manager is no longer
      // valid.
      if (focus_tracker_.get() != NULL)
        focus_tracker_->SetFocusManager(NULL);
    }
  }
}

void InfoBarItemView::AddChildView(ChromeViews::View* view) {
  AddChildViewTrailing(view, kUnrelatedControlHorizontalSpacing);
}

void InfoBarItemView::AddChildView(int index, ChromeViews::View* view) {
  if (index < insert_index_)
    AddChildViewLeading(view);
  else
    AddChildViewTrailing(view);
}

void InfoBarItemView::RemoveChildView(ChromeViews::View* view) {
  int index = GetChildIndex(view);
  if (index >= 0) {
    if (index < insert_index_) {
      // We're removing a leading view. So the view at index + 1 (immediately
      // trailing) is the corresponding spacer view.
      View* spacer_view = GetChildViewAt(index + 1);
      ChromeViews::View::RemoveChildView(view);
      ChromeViews::View::RemoveChildView(spacer_view);
      delete spacer_view;
      // Need to change the insert_index_ so it is still pointing at the
      // "middle" index between left and right aligned views.
      insert_index_ -= 2;
    } else {
      // We're removing a trailing view. So the view at index - 1 (immediately
      // leading) is the corresponding spacer view.
      View* spacer_view = GetChildViewAt(index - 1);
      ChromeViews::View::RemoveChildView(view);
      ChromeViews::View::RemoveChildView(spacer_view);
      delete spacer_view;
    }
  }
}

void InfoBarItemView::ButtonPressed(ChromeViews::BaseButton* button) {
  if (button == close_button_)
    CloseButtonPressed();
}

void InfoBarItemView::AnimationProgressed(const Animation* animation) {
  static_cast<InfoBarView*>(GetParent())->ChildAnimationProgressed();
}

void InfoBarItemView::AnimationEnded(const Animation* animation) {
  static_cast<InfoBarView*>(GetParent())->ChildAnimationEnded();

  if (!animation_->IsShowing())
    Close();
}

void InfoBarItemView::Init() {
  ResourceBundle &rb = ResourceBundle::GetSharedInstance();
  close_button_ = new ChromeViews::Button();
  close_button_->SetImage(ChromeViews::Button::BS_NORMAL,
                          rb.GetBitmapNamed(IDR_CLOSE_BAR));
  close_button_->SetImage(ChromeViews::Button::BS_HOT,
                          rb.GetBitmapNamed(IDR_CLOSE_BAR_H));
  close_button_->SetImage(ChromeViews::Button::BS_PUSHED,
                          rb.GetBitmapNamed(IDR_CLOSE_BAR_P));
  close_button_->SetListener(this, 0);
  close_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE));
  AddChildViewTrailing(close_button_);

  animation_.reset(new SlideAnimation(this));
  animation_->SetTweenType(SlideAnimation::NONE);
  animation_->Show();
}