OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h" | 5 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 #include <dlfcn.h> | 8 #include <dlfcn.h> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
12 #include "base/memory/ref_counted_memory.h" | 12 #include "base/memory/ref_counted_memory.h" |
13 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
14 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
15 #include "base/threading/sequenced_worker_pool.h" | 15 #include "base/threading/sequenced_worker_pool.h" |
16 #include "chrome/browser/ui/libgtk2ui/menu_util.h" | 16 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h" |
17 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
18 #include "ui/base/models/menu_model.h" | 18 #include "ui/base/models/menu_model.h" |
| 19 #include "ui/gfx/image/image.h" |
19 #include "ui/gfx/image/image_skia.h" | 20 #include "ui/gfx/image/image_skia.h" |
20 | 21 |
21 namespace { | 22 namespace { |
22 | 23 |
23 typedef enum { | 24 typedef enum { |
24 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, | 25 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, |
25 APP_INDICATOR_CATEGORY_COMMUNICATIONS, | 26 APP_INDICATOR_CATEGORY_COMMUNICATIONS, |
26 APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, | 27 APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, |
27 APP_INDICATOR_CATEGORY_HARDWARE, | 28 APP_INDICATOR_CATEGORY_HARDWARE, |
28 APP_INDICATOR_CATEGORY_OTHER | 29 APP_INDICATOR_CATEGORY_OTHER |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 | 161 |
161 } // namespace | 162 } // namespace |
162 | 163 |
163 namespace libgtk2ui { | 164 namespace libgtk2ui { |
164 | 165 |
165 AppIndicatorIcon::AppIndicatorIcon(std::string id, | 166 AppIndicatorIcon::AppIndicatorIcon(std::string id, |
166 const gfx::ImageSkia& image, | 167 const gfx::ImageSkia& image, |
167 const base::string16& tool_tip) | 168 const base::string16& tool_tip) |
168 : id_(id), | 169 : id_(id), |
169 icon_(NULL), | 170 icon_(NULL), |
170 gtk_menu_(NULL), | |
171 menu_model_(NULL), | 171 menu_model_(NULL), |
172 icon_change_count_(0), | 172 icon_change_count_(0), |
173 block_activation_(false), | |
174 weak_factory_(this) { | 173 weak_factory_(this) { |
175 EnsureMethodsLoaded(); | 174 EnsureMethodsLoaded(); |
176 tool_tip_ = base::UTF16ToUTF8(tool_tip); | 175 tool_tip_ = base::UTF16ToUTF8(tool_tip); |
177 SetImage(image); | 176 SetImage(image); |
178 } | 177 } |
179 AppIndicatorIcon::~AppIndicatorIcon() { | 178 AppIndicatorIcon::~AppIndicatorIcon() { |
180 if (icon_) { | 179 if (icon_) { |
181 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); | 180 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); |
182 if (gtk_menu_) | |
183 DestroyMenu(); | |
184 g_object_unref(icon_); | 181 g_object_unref(icon_); |
185 content::BrowserThread::GetBlockingPool()->PostTask( | 182 content::BrowserThread::GetBlockingPool()->PostTask( |
186 FROM_HERE, | 183 FROM_HERE, |
187 base::Bind(&DeleteTempImagePath, icon_file_path_.DirName())); | 184 base::Bind(&DeleteTempImagePath, icon_file_path_.DirName())); |
188 } | 185 } |
189 } | 186 } |
190 | 187 |
191 // static | 188 // static |
192 bool AppIndicatorIcon::CouldOpen() { | 189 bool AppIndicatorIcon::CouldOpen() { |
193 EnsureMethodsLoaded(); | 190 EnsureMethodsLoaded(); |
(...skipping 23 matching lines...) Expand all Loading... |
217 } | 214 } |
218 | 215 |
219 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) { | 216 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) { |
220 // Ignore pressed images, since the standard on Linux is to not highlight | 217 // Ignore pressed images, since the standard on Linux is to not highlight |
221 // pressed status icons. | 218 // pressed status icons. |
222 } | 219 } |
223 | 220 |
224 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { | 221 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { |
225 DCHECK(!tool_tip_.empty()); | 222 DCHECK(!tool_tip_.empty()); |
226 tool_tip_ = base::UTF16ToUTF8(tool_tip); | 223 tool_tip_ = base::UTF16ToUTF8(tool_tip); |
227 | 224 UpdateClickActionReplacementMenuItem(); |
228 // We can set the click action label only if the icon exists. Also we only | |
229 // need to update the label if it is shown and it's only shown if we are sure | |
230 // that there is a click action or if there is no menu. | |
231 if (icon_ && (delegate()->HasClickAction() || menu_model_ == NULL)) { | |
232 GList* children = gtk_container_get_children(GTK_CONTAINER(gtk_menu_)); | |
233 for (GList* child = children; child; child = g_list_next(child)) | |
234 if (g_object_get_data(G_OBJECT(child->data), "click-action-item") != | |
235 NULL) { | |
236 gtk_menu_item_set_label(GTK_MENU_ITEM(child->data), | |
237 tool_tip_.c_str()); | |
238 break; | |
239 } | |
240 g_list_free(children); | |
241 } | |
242 } | 225 } |
243 | 226 |
244 void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) { | 227 void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) { |
245 if (!g_opened) | 228 if (!g_opened) |
246 return; | 229 return; |
247 | 230 |
248 if (gtk_menu_) { | |
249 DestroyMenu(); | |
250 } | |
251 menu_model_ = model; | 231 menu_model_ = model; |
252 | 232 |
253 // The icon is created asynchronously so it might not exist when the menu is | 233 // The icon is created asynchronously so it might not exist when the menu is |
254 // set. | 234 // set. |
255 if (icon_) | 235 if (icon_) |
256 SetMenu(); | 236 SetMenu(); |
257 } | 237 } |
258 | 238 |
259 void AppIndicatorIcon::RefreshPlatformContextMenu() { | 239 void AppIndicatorIcon::RefreshPlatformContextMenu() { |
260 gtk_container_foreach( | 240 menu_->Refresh(); |
261 GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_); | |
262 } | 241 } |
263 | 242 |
264 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { | 243 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { |
265 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 244 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
266 if (icon_file_path.empty()) | 245 if (icon_file_path.empty()) |
267 return; | 246 return; |
268 | 247 |
269 base::FilePath old_path = icon_file_path_; | 248 base::FilePath old_path = icon_file_path_; |
270 icon_file_path_ = icon_file_path; | 249 icon_file_path_ = icon_file_path; |
271 | 250 |
(...skipping 15 matching lines...) Expand all Loading... |
287 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon"); | 266 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon"); |
288 | 267 |
289 // Delete previous icon directory. | 268 // Delete previous icon directory. |
290 content::BrowserThread::GetBlockingPool()->PostTask( | 269 content::BrowserThread::GetBlockingPool()->PostTask( |
291 FROM_HERE, | 270 FROM_HERE, |
292 base::Bind(&DeleteTempImagePath, old_path.DirName())); | 271 base::Bind(&DeleteTempImagePath, old_path.DirName())); |
293 } | 272 } |
294 } | 273 } |
295 | 274 |
296 void AppIndicatorIcon::SetMenu() { | 275 void AppIndicatorIcon::SetMenu() { |
297 gtk_menu_ = gtk_menu_new(); | 276 menu_.reset(new AppIndicatorIconMenu(menu_model_)); |
298 | 277 UpdateClickActionReplacementMenuItem(); |
299 if (delegate()->HasClickAction() || menu_model_ == NULL) { | 278 app_indicator_set_menu(icon_, GTK_MENU(menu_->GetGtkMenu())); |
300 CreateClickActionReplacement(); | |
301 if (menu_model_) { | |
302 // Add separator before the other menu items. | |
303 GtkWidget* menu_item = gtk_separator_menu_item_new(); | |
304 gtk_widget_show(menu_item); | |
305 gtk_menu_shell_append(GTK_MENU_SHELL(gtk_menu_), menu_item); | |
306 } | |
307 } | |
308 if (menu_model_) { | |
309 BuildSubmenuFromModel(menu_model_, | |
310 gtk_menu_, | |
311 G_CALLBACK(OnMenuItemActivatedThunk), | |
312 &block_activation_, | |
313 this); | |
314 RefreshPlatformContextMenu(); | |
315 } | |
316 app_indicator_set_menu(icon_, GTK_MENU(gtk_menu_)); | |
317 } | 279 } |
318 | 280 |
319 void AppIndicatorIcon::CreateClickActionReplacement() { | 281 void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() { |
| 282 // The menu may not have been created yet. |
| 283 if (!menu_.get()) |
| 284 return; |
| 285 |
| 286 if (!delegate()->HasClickAction() && menu_model_) |
| 287 return; |
| 288 |
320 DCHECK(!tool_tip_.empty()); | 289 DCHECK(!tool_tip_.empty()); |
321 | 290 menu_->UpdateClickActionReplacementMenuItem( |
322 // Add "click replacement menu item". | 291 tool_tip_.c_str(), |
323 GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(tool_tip_.c_str()); | 292 base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated, |
324 g_object_set_data( | 293 base::Unretained(this))); |
325 G_OBJECT(menu_item), "click-action-item", GINT_TO_POINTER(1)); | |
326 g_signal_connect(menu_item, "activate", G_CALLBACK(OnClickThunk), this); | |
327 gtk_widget_show(menu_item); | |
328 gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item); | |
329 } | 294 } |
330 | 295 |
331 void AppIndicatorIcon::DestroyMenu() { | 296 void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() { |
332 gtk_widget_destroy(gtk_menu_); | |
333 gtk_menu_ = NULL; | |
334 menu_model_ = NULL; | |
335 } | |
336 | |
337 void AppIndicatorIcon::OnClick(GtkWidget* menu_item) { | |
338 if (delegate()) | 297 if (delegate()) |
339 delegate()->OnClick(); | 298 delegate()->OnClick(); |
340 } | 299 } |
341 | 300 |
342 void AppIndicatorIcon::OnMenuItemActivated(GtkWidget* menu_item) { | |
343 if (block_activation_) | |
344 return; | |
345 | |
346 ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item)); | |
347 if (!model) { | |
348 // There won't be a model for "native" submenus like the "Input Methods" | |
349 // context menu. We don't need to handle activation messages for submenus | |
350 // anyway, so we can just return here. | |
351 DCHECK(gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item))); | |
352 return; | |
353 } | |
354 | |
355 // The activate signal is sent to radio items as they get deselected; | |
356 // ignore it in this case. | |
357 if (GTK_IS_RADIO_MENU_ITEM(menu_item) && | |
358 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) { | |
359 return; | |
360 } | |
361 | |
362 int id; | |
363 if (!GetMenuItemID(menu_item, &id)) | |
364 return; | |
365 | |
366 // The menu item can still be activated by hotkeys even if it is disabled. | |
367 if (menu_model_->IsEnabledAt(id)) | |
368 ExecuteCommand(model, id); | |
369 } | |
370 | |
371 } // namespace libgtk2ui | 301 } // namespace libgtk2ui |
OLD | NEW |