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/display_settings_provider_win.h"
6
7#include <shellapi.h>
8
9#include "base/logging.h"
10#include "ui/views/widget/monitor_win.h"
11
12namespace {
13// The thickness of the area of an auto-hiding taskbar that is still visible
14// when the taskbar becomes hidden.
15const int kHiddenAutoHideTaskbarThickness = 2;
16
17// The polling interval to check auto-hiding taskbars.
18const int kCheckTaskbarPollingIntervalMs = 500;
19}  // namespace
20
21DisplaySettingsProviderWin::DisplaySettingsProviderWin()
22    : monitor_(NULL) {
23  memset(taskbars_, 0, sizeof(taskbars_));
24}
25
26DisplaySettingsProviderWin::~DisplaySettingsProviderWin() {
27}
28
29void DisplaySettingsProviderWin::OnDisplaySettingsChanged() {
30  DisplaySettingsProvider::OnDisplaySettingsChanged();
31
32  gfx::Rect primary_work_area = GetPrimaryWorkArea();
33  RECT rect = primary_work_area.ToRECT();
34  monitor_ = ::MonitorFromRect(&rect, MONITOR_DEFAULTTOPRIMARY);
35  DCHECK(monitor_);
36
37  bool taskbar_exists = CheckTaskbars(false);
38
39  // If no auto-hiding taskbar exists, we do not need to start the polling
40  // timer. If a taskbar is then set to auto-hiding, UpdateWorkArea will be
41  // called due to the work area change.
42  if (taskbar_exists) {
43    if (!polling_timer_.IsRunning()) {
44      polling_timer_.Start(FROM_HERE,
45          base::TimeDelta::FromMilliseconds(kCheckTaskbarPollingIntervalMs),
46          this,
47          &DisplaySettingsProviderWin::OnPollingTimer);
48    }
49  } else {
50    if (polling_timer_.IsRunning())
51      polling_timer_.Stop();
52  }
53}
54
55bool DisplaySettingsProviderWin::IsAutoHidingDesktopBarEnabled(
56    DesktopBarAlignment alignment) {
57  CheckTaskbars(false);
58  return taskbars_[static_cast<int>(alignment)].window != NULL;
59}
60
61int DisplaySettingsProviderWin::GetDesktopBarThickness(
62    DesktopBarAlignment alignment) const {
63  return GetDesktopBarThicknessFromBounds(alignment, GetBounds(alignment));
64}
65
66DisplaySettingsProvider::DesktopBarVisibility
67DisplaySettingsProviderWin::GetDesktopBarVisibility(
68    DesktopBarAlignment alignment) const {
69  return GetDesktopBarVisibilityFromBounds(alignment, GetBounds(alignment));
70}
71
72gfx::Rect DisplaySettingsProviderWin::GetBounds(
73    DesktopBarAlignment alignment) const {
74  HWND taskbar_window = taskbars_[static_cast<int>(alignment)].window;
75  if (!taskbar_window)
76    return gfx::Rect();
77
78  RECT rect;
79  if (!::GetWindowRect(taskbar_window, &rect))
80    return gfx::Rect();
81  return gfx::Rect(rect);
82}
83
84int DisplaySettingsProviderWin::GetDesktopBarThicknessFromBounds(
85    DesktopBarAlignment alignment,
86    const gfx::Rect& taskbar_bounds) const {
87  switch (alignment) {
88    case DESKTOP_BAR_ALIGNED_BOTTOM:
89      return taskbar_bounds.height();
90    case DESKTOP_BAR_ALIGNED_LEFT:
91    case DESKTOP_BAR_ALIGNED_RIGHT:
92      return taskbar_bounds.width();
93    default:
94      NOTREACHED();
95      return 0;
96  }
97}
98
99DisplaySettingsProvider::DesktopBarVisibility
100DisplaySettingsProviderWin::GetDesktopBarVisibilityFromBounds(
101    DesktopBarAlignment alignment,
102    const gfx::Rect& taskbar_bounds) const {
103  gfx::Rect primary_work_area = GetPrimaryWorkArea();
104  switch (alignment) {
105    case DESKTOP_BAR_ALIGNED_BOTTOM:
106      if (taskbar_bounds.bottom() <= primary_work_area.bottom())
107        return DESKTOP_BAR_VISIBLE;
108      else if (taskbar_bounds.y() >=
109               primary_work_area.bottom() - kHiddenAutoHideTaskbarThickness)
110        return DESKTOP_BAR_HIDDEN;
111      else
112        return DESKTOP_BAR_ANIMATING;
113
114    case DESKTOP_BAR_ALIGNED_LEFT:
115      if (taskbar_bounds.x() >= primary_work_area.x())
116        return DESKTOP_BAR_VISIBLE;
117      else if (taskbar_bounds.right() <=
118               primary_work_area.x() + kHiddenAutoHideTaskbarThickness)
119        return DESKTOP_BAR_HIDDEN;
120      else
121        return DESKTOP_BAR_ANIMATING;
122
123    case DESKTOP_BAR_ALIGNED_RIGHT:
124      if (taskbar_bounds.right() <= primary_work_area.right())
125        return DESKTOP_BAR_VISIBLE;
126      else if (taskbar_bounds.x() >=
127               primary_work_area.right() - kHiddenAutoHideTaskbarThickness)
128        return DESKTOP_BAR_HIDDEN;
129      else
130        return DESKTOP_BAR_ANIMATING;
131
132    default:
133      NOTREACHED();
134      return DESKTOP_BAR_VISIBLE;
135  }
136}
137
138void DisplaySettingsProviderWin::OnPollingTimer() {
139  CheckTaskbars(true);
140}
141
142bool DisplaySettingsProviderWin::CheckTaskbars(bool notify_observer) {
143  bool taskbar_exists = false;
144  UINT edges[] = { ABE_BOTTOM };
145  for (size_t i = 0; i < kMaxTaskbars; ++i) {
146    taskbars_[i].window =
147        views::GetTopmostAutoHideTaskbarForEdge(edges[i], monitor_);
148    if (taskbars_[i].window)
149      taskbar_exists = true;
150  }
151  if (!taskbar_exists) {
152    for (size_t i = 0; i < kMaxTaskbars; ++i) {
153      taskbars_[i].thickness = 0;
154      taskbars_[i].visibility = DESKTOP_BAR_HIDDEN;
155    }
156    return false;
157  }
158
159  for (size_t i = 0; i < kMaxTaskbars; ++i) {
160    DesktopBarAlignment alignment = static_cast<DesktopBarAlignment>(i);
161
162    gfx::Rect bounds = GetBounds(alignment);
163
164    // Check the thickness change.
165    int thickness = GetDesktopBarThicknessFromBounds(alignment, bounds);
166    if (thickness != taskbars_[i].thickness) {
167      taskbars_[i].thickness = thickness;
168      if (notify_observer) {
169        FOR_EACH_OBSERVER(
170            DesktopBarObserver,
171            desktop_bar_observers(),
172            OnAutoHidingDesktopBarThicknessChanged(alignment, thickness));
173      }
174    }
175
176    // Check and notify the visibility change.
177    DesktopBarVisibility visibility = GetDesktopBarVisibilityFromBounds(
178        alignment, bounds);
179    if (visibility != taskbars_[i].visibility) {
180      taskbars_[i].visibility = visibility;
181      if (notify_observer) {
182        FOR_EACH_OBSERVER(
183            DesktopBarObserver,
184            desktop_bar_observers(),
185            OnAutoHidingDesktopBarVisibilityChanged(alignment, visibility));
186      }
187    }
188  }
189
190  return true;
191}
192
193// static
194DisplaySettingsProvider* DisplaySettingsProvider::Create() {
195  return new DisplaySettingsProviderWin();
196}
197