MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / chrome / browser / ui / views / status_icons / status_tray_win.cc
blob44148d5640aa9bec8124b4ea5116d470f7b3de3f
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.
5 #include "chrome/browser/ui/views/status_icons/status_tray_win.h"
7 #include <commctrl.h>
9 #include "base/bind.h"
10 #include "base/threading/non_thread_safe.h"
11 #include "base/threading/thread.h"
12 #include "base/win/wrapped_window_proc.h"
13 #include "chrome/browser/ui/views/status_icons/status_icon_win.h"
14 #include "chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h"
15 #include "chrome/common/chrome_constants.h"
16 #include "ui/gfx/screen.h"
17 #include "ui/gfx/win/hwnd_util.h"
19 static const UINT kStatusIconMessage = WM_APP + 1;
21 namespace {
22 // |kBaseIconId| is 2 to avoid conflicts with plugins that hard-code id 1.
23 const UINT kBaseIconId = 2;
25 UINT ReservedIconId(StatusTray::StatusIconType type) {
26 return kBaseIconId + static_cast<UINT>(type);
28 } // namespace
30 // Default implementation for StatusTrayStateChanger that communicates to
31 // Exporer.exe via COM. It spawns a background thread with a fresh COM
32 // apartment and requests that the visibility be increased unless the user
33 // has explicitly set the icon to be hidden.
34 class StatusTrayStateChangerProxyImpl : public StatusTrayStateChangerProxy,
35 public base::NonThreadSafe {
36 public:
37 StatusTrayStateChangerProxyImpl()
38 : pending_requests_(0),
39 worker_thread_("StatusIconCOMWorkerThread"),
40 weak_factory_(this) {
41 worker_thread_.init_com_with_mta(false);
44 virtual void EnqueueChange(UINT icon_id, HWND window) override {
45 DCHECK(CalledOnValidThread());
46 if (pending_requests_ == 0)
47 worker_thread_.Start();
49 ++pending_requests_;
50 worker_thread_.message_loop_proxy()->PostTaskAndReply(
51 FROM_HERE,
52 base::Bind(
53 &StatusTrayStateChangerProxyImpl::EnqueueChangeOnWorkerThread,
54 icon_id,
55 window),
56 base::Bind(&StatusTrayStateChangerProxyImpl::ChangeDone,
57 weak_factory_.GetWeakPtr()));
60 private:
61 // Must be called only on |worker_thread_|, to ensure the correct COM
62 // apartment.
63 static void EnqueueChangeOnWorkerThread(UINT icon_id, HWND window) {
64 // It appears that IUnknowns are coincidentally compatible with
65 // scoped_refptr. Normally I wouldn't depend on that but it seems that
66 // base::win::IUnknownImpl itself depends on that coincidence so it's
67 // already being assumed elsewhere.
68 scoped_refptr<StatusTrayStateChangerWin> status_tray_state_changer(
69 new StatusTrayStateChangerWin(icon_id, window));
70 status_tray_state_changer->EnsureTrayIconVisible();
73 // Called on UI thread.
74 void ChangeDone() {
75 DCHECK(CalledOnValidThread());
76 DCHECK_GT(pending_requests_, 0);
78 if (--pending_requests_ == 0)
79 worker_thread_.Stop();
82 private:
83 int pending_requests_;
84 base::Thread worker_thread_;
85 base::WeakPtrFactory<StatusTrayStateChangerProxyImpl> weak_factory_;
87 DISALLOW_COPY_AND_ASSIGN(StatusTrayStateChangerProxyImpl);
90 StatusTrayWin::StatusTrayWin()
91 : next_icon_id_(1),
92 atom_(0),
93 instance_(NULL),
94 window_(NULL) {
95 // Register our window class
96 WNDCLASSEX window_class;
97 base::win::InitializeWindowClass(
98 chrome::kStatusTrayWindowClass,
99 &base::win::WrappedWindowProc<StatusTrayWin::WndProcStatic>,
100 0, 0, 0, NULL, NULL, NULL, NULL, NULL,
101 &window_class);
102 instance_ = window_class.hInstance;
103 atom_ = RegisterClassEx(&window_class);
104 CHECK(atom_);
106 // If the taskbar is re-created after we start up, we have to rebuild all of
107 // our icons.
108 taskbar_created_message_ = RegisterWindowMessage(TEXT("TaskbarCreated"));
110 // Create an offscreen window for handling messages for the status icons. We
111 // create a hidden WS_POPUP window instead of an HWND_MESSAGE window, because
112 // only top-level windows such as popups can receive broadcast messages like
113 // "TaskbarCreated".
114 window_ = CreateWindow(MAKEINTATOM(atom_),
115 0, WS_POPUP, 0, 0, 0, 0, 0, 0, instance_, 0);
116 gfx::CheckWindowCreated(window_);
117 gfx::SetWindowUserData(window_, this);
120 StatusTrayWin::~StatusTrayWin() {
121 if (window_)
122 DestroyWindow(window_);
124 if (atom_)
125 UnregisterClass(MAKEINTATOM(atom_), instance_);
128 void StatusTrayWin::UpdateIconVisibilityInBackground(
129 StatusIconWin* status_icon) {
130 if (!state_changer_proxy_.get())
131 state_changer_proxy_.reset(new StatusTrayStateChangerProxyImpl);
133 state_changer_proxy_->EnqueueChange(status_icon->icon_id(),
134 status_icon->window());
137 LRESULT CALLBACK StatusTrayWin::WndProcStatic(HWND hwnd,
138 UINT message,
139 WPARAM wparam,
140 LPARAM lparam) {
141 StatusTrayWin* msg_wnd = reinterpret_cast<StatusTrayWin*>(
142 GetWindowLongPtr(hwnd, GWLP_USERDATA));
143 if (msg_wnd)
144 return msg_wnd->WndProc(hwnd, message, wparam, lparam);
145 else
146 return ::DefWindowProc(hwnd, message, wparam, lparam);
149 LRESULT CALLBACK StatusTrayWin::WndProc(HWND hwnd,
150 UINT message,
151 WPARAM wparam,
152 LPARAM lparam) {
153 if (message == taskbar_created_message_) {
154 // We need to reset all of our icons because the taskbar went away.
155 for (StatusIcons::const_iterator i(status_icons().begin());
156 i != status_icons().end(); ++i) {
157 StatusIconWin* win_icon = static_cast<StatusIconWin*>(*i);
158 win_icon->ResetIcon();
160 return TRUE;
161 } else if (message == kStatusIconMessage) {
162 StatusIconWin* win_icon = NULL;
164 // Find the selected status icon.
165 for (StatusIcons::const_iterator i(status_icons().begin());
166 i != status_icons().end();
167 ++i) {
168 StatusIconWin* current_win_icon = static_cast<StatusIconWin*>(*i);
169 if (current_win_icon->icon_id() == wparam) {
170 win_icon = current_win_icon;
171 break;
175 // It is possible for this procedure to be called with an obsolete icon
176 // id. In that case we should just return early before handling any
177 // actions.
178 if (!win_icon)
179 return TRUE;
181 switch (lparam) {
182 case TB_INDETERMINATE:
183 win_icon->HandleBalloonClickEvent();
184 return TRUE;
186 case WM_LBUTTONDOWN:
187 case WM_RBUTTONDOWN:
188 case WM_CONTEXTMENU:
189 // Walk our icons, find which one was clicked on, and invoke its
190 // HandleClickEvent() method.
191 gfx::Point cursor_pos(
192 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint());
193 win_icon->HandleClickEvent(cursor_pos, lparam == WM_LBUTTONDOWN);
194 return TRUE;
197 return ::DefWindowProc(hwnd, message, wparam, lparam);
200 StatusIcon* StatusTrayWin::CreatePlatformStatusIcon(
201 StatusTray::StatusIconType type,
202 const gfx::ImageSkia& image,
203 const base::string16& tool_tip) {
204 UINT next_icon_id;
205 if (type == StatusTray::OTHER_ICON)
206 next_icon_id = NextIconId();
207 else
208 next_icon_id = ReservedIconId(type);
210 StatusIcon* icon =
211 new StatusIconWin(this, next_icon_id, window_, kStatusIconMessage);
213 icon->SetImage(image);
214 icon->SetToolTip(tool_tip);
215 return icon;
218 UINT StatusTrayWin::NextIconId() {
219 UINT icon_id = next_icon_id_++;
220 return kBaseIconId + static_cast<UINT>(NAMED_STATUS_ICON_COUNT) + icon_id;
223 void StatusTrayWin::SetStatusTrayStateChangerProxyForTest(
224 scoped_ptr<StatusTrayStateChangerProxy> proxy) {
225 state_changer_proxy_ = proxy.Pass();
228 StatusTray* StatusTray::Create() {
229 return new StatusTrayWin();