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 "chrome/browser/ui/panels/panel_resize_controller.h"
6
7#include "base/logging.h"
8#include "chrome/browser/ui/panels/panel.h"
9#include "chrome/browser/ui/panels/panel_manager.h"
10
11namespace {
12  static bool ResizingLeft(panel::ResizingSides sides) {
13    return sides == panel::RESIZE_TOP_LEFT ||
14           sides == panel::RESIZE_LEFT ||
15           sides == panel::RESIZE_BOTTOM_LEFT;
16  }
17
18  static bool ResizingRight(panel::ResizingSides sides) {
19    return sides == panel::RESIZE_TOP_RIGHT ||
20           sides == panel::RESIZE_RIGHT ||
21           sides == panel::RESIZE_BOTTOM_RIGHT;
22  }
23
24  static bool ResizingTop(panel::ResizingSides sides) {
25    return sides == panel::RESIZE_TOP_LEFT ||
26           sides == panel::RESIZE_TOP ||
27           sides == panel::RESIZE_TOP_RIGHT;
28  }
29
30  static bool ResizingBottom(panel::ResizingSides sides) {
31    return sides == panel::RESIZE_BOTTOM_RIGHT ||
32           sides == panel::RESIZE_BOTTOM ||
33           sides == panel::RESIZE_BOTTOM_LEFT;
34  }
35}
36
37PanelResizeController::PanelResizeController(PanelManager* panel_manager)
38  : panel_manager_(panel_manager),
39    resizing_panel_(NULL),
40    sides_resized_(panel::RESIZE_NONE) {
41}
42
43void PanelResizeController::StartResizing(Panel* panel,
44                                          const gfx::Point& mouse_location,
45                                          panel::ResizingSides sides) {
46  DCHECK(!IsResizing());
47  DCHECK_NE(panel::RESIZE_NONE, sides);
48
49  panel::Resizability resizability = panel->CanResizeByMouse();
50  DCHECK_NE(panel::NOT_RESIZABLE, resizability);
51  panel::Resizability resizability_to_test;
52  switch (sides) {
53    case panel::RESIZE_TOP_LEFT:
54      resizability_to_test = panel::RESIZABLE_TOP_LEFT;
55      break;
56    case panel::RESIZE_TOP:
57      resizability_to_test = panel::RESIZABLE_TOP;
58      break;
59    case panel::RESIZE_TOP_RIGHT:
60      resizability_to_test = panel::RESIZABLE_TOP_RIGHT;
61      break;
62    case panel::RESIZE_LEFT:
63      resizability_to_test = panel::RESIZABLE_LEFT;
64      break;
65    case panel::RESIZE_RIGHT:
66      resizability_to_test = panel::RESIZABLE_RIGHT;
67      break;
68    case panel::RESIZE_BOTTOM_LEFT:
69      resizability_to_test = panel::RESIZABLE_BOTTOM_LEFT;
70      break;
71    case panel::RESIZE_BOTTOM:
72      resizability_to_test = panel::RESIZABLE_BOTTOM;
73      break;
74    case panel::RESIZE_BOTTOM_RIGHT:
75      resizability_to_test = panel::RESIZABLE_BOTTOM_RIGHT;
76      break;
77    default:
78      resizability_to_test = panel::NOT_RESIZABLE;
79      break;
80  }
81  if ((resizability & resizability_to_test) == 0) {
82    DLOG(WARNING) << "Resizing not allowed. Is this a test?";
83    return;
84  }
85
86  mouse_location_at_start_ = mouse_location;
87  bounds_at_start_ = panel->GetBounds();
88  sides_resized_ = sides;
89  resizing_panel_ = panel;
90  resizing_panel_->OnPanelStartUserResizing();
91}
92
93void PanelResizeController::Resize(const gfx::Point& mouse_location) {
94  DCHECK(IsResizing());
95  panel::Resizability resizability = resizing_panel_->CanResizeByMouse();
96  if (panel::NOT_RESIZABLE == resizability) {
97    EndResizing(false);
98    return;
99  }
100  gfx::Rect bounds = resizing_panel_->GetBounds();
101
102  if (ResizingRight(sides_resized_)) {
103    bounds.set_width(std::max(bounds_at_start_.width() +
104                     mouse_location.x() - mouse_location_at_start_.x(), 0));
105  }
106  if (ResizingBottom(sides_resized_)) {
107    bounds.set_height(std::max(bounds_at_start_.height() +
108                      mouse_location.y() - mouse_location_at_start_.y(), 0));
109  }
110  if (ResizingLeft(sides_resized_)) {
111    bounds.set_width(std::max(bounds_at_start_.width() +
112                     mouse_location_at_start_.x() - mouse_location.x(), 0));
113  }
114  if (ResizingTop(sides_resized_)) {
115    int new_height = std::max(bounds_at_start_.height() +
116                     mouse_location_at_start_.y() - mouse_location.y(), 0);
117    int new_y = bounds_at_start_.bottom() - new_height;
118
119    // Make sure that the panel's titlebar cannot be resized under the taskbar
120    // or OSX menu bar that is aligned to top screen edge.
121    gfx::Rect display_area = panel_manager_->display_settings_provider()->
122        GetDisplayAreaMatching(bounds);
123    gfx::Rect work_area = panel_manager_->display_settings_provider()->
124        GetWorkAreaMatching(bounds);
125    if (display_area.y() <= mouse_location.y() &&
126        mouse_location.y() < work_area.y()) {
127      new_height -= work_area.y() - new_y;
128    }
129
130    bounds.set_height(new_height);
131  }
132
133  resizing_panel_->IncreaseMaxSize(bounds.size());
134
135  // This effectively only clamps using the min size, since the max_size was
136  // updated above.
137  bounds.set_size(resizing_panel_->ClampSize(bounds.size()));
138
139  if (ResizingLeft(sides_resized_))
140    bounds.set_x(bounds_at_start_.right() - bounds.width());
141
142  if (ResizingTop(sides_resized_))
143    bounds.set_y(bounds_at_start_.bottom() - bounds.height());
144
145  if (bounds != resizing_panel_->GetBounds()) {
146    resizing_panel_->SetPanelBoundsInstantly(bounds);
147    resizing_panel_->OnWindowResizedByMouse(bounds);
148  }
149}
150
151Panel* PanelResizeController::EndResizing(bool cancelled) {
152  DCHECK(IsResizing());
153
154  if (cancelled) {
155    resizing_panel_->SetPanelBoundsInstantly(bounds_at_start_);
156    resizing_panel_->OnWindowResizedByMouse(bounds_at_start_);
157  }
158
159  // Do a thorough cleanup.
160  resizing_panel_->OnPanelEndUserResizing();
161  Panel* resized_panel = resizing_panel_;
162  resizing_panel_ = NULL;
163  sides_resized_ = panel::RESIZE_NONE;
164  bounds_at_start_ = gfx::Rect();
165  mouse_location_at_start_ = gfx::Point();
166  return resized_panel;
167}
168
169void PanelResizeController::OnPanelClosed(Panel* panel) {
170  if (!resizing_panel_)
171    return;
172
173  // If the resizing panel is closed, abort the resize operation.
174  if (resizing_panel_ == panel)
175    EndResizing(false);
176}
177