| 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/app_indicator_icon_menu.h" | 16 #include "chrome/browser/ui/libgtk2ui/menu_util.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" | |
| 20 #include "ui/gfx/image/image_skia.h" | 19 #include "ui/gfx/image/image_skia.h" |
| 21 | 20 |
| 22 namespace { | 21 namespace { |
| 23 | 22 |
| 24 typedef enum { | 23 typedef enum { |
| 25 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, | 24 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, |
| 26 APP_INDICATOR_CATEGORY_COMMUNICATIONS, | 25 APP_INDICATOR_CATEGORY_COMMUNICATIONS, |
| 27 APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, | 26 APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, |
| 28 APP_INDICATOR_CATEGORY_HARDWARE, | 27 APP_INDICATOR_CATEGORY_HARDWARE, |
| 29 APP_INDICATOR_CATEGORY_OTHER | 28 APP_INDICATOR_CATEGORY_OTHER |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 161 | 160 |
| 162 } // namespace | 161 } // namespace |
| 163 | 162 |
| 164 namespace libgtk2ui { | 163 namespace libgtk2ui { |
| 165 | 164 |
| 166 AppIndicatorIcon::AppIndicatorIcon(std::string id, | 165 AppIndicatorIcon::AppIndicatorIcon(std::string id, |
| 167 const gfx::ImageSkia& image, | 166 const gfx::ImageSkia& image, |
| 168 const base::string16& tool_tip) | 167 const base::string16& tool_tip) |
| 169 : id_(id), | 168 : id_(id), |
| 170 icon_(NULL), | 169 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), |
| 173 weak_factory_(this) { | 174 weak_factory_(this) { |
| 174 EnsureMethodsLoaded(); | 175 EnsureMethodsLoaded(); |
| 175 tool_tip_ = base::UTF16ToUTF8(tool_tip); | 176 tool_tip_ = base::UTF16ToUTF8(tool_tip); |
| 176 SetImage(image); | 177 SetImage(image); |
| 177 } | 178 } |
| 178 AppIndicatorIcon::~AppIndicatorIcon() { | 179 AppIndicatorIcon::~AppIndicatorIcon() { |
| 179 if (icon_) { | 180 if (icon_) { |
| 180 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); | 181 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); |
| 182 if (gtk_menu_) |
| 183 DestroyMenu(); |
| 181 g_object_unref(icon_); | 184 g_object_unref(icon_); |
| 182 content::BrowserThread::GetBlockingPool()->PostTask( | 185 content::BrowserThread::GetBlockingPool()->PostTask( |
| 183 FROM_HERE, | 186 FROM_HERE, |
| 184 base::Bind(&DeleteTempImagePath, icon_file_path_.DirName())); | 187 base::Bind(&DeleteTempImagePath, icon_file_path_.DirName())); |
| 185 } | 188 } |
| 186 } | 189 } |
| 187 | 190 |
| 188 // static | 191 // static |
| 189 bool AppIndicatorIcon::CouldOpen() { | 192 bool AppIndicatorIcon::CouldOpen() { |
| 190 EnsureMethodsLoaded(); | 193 EnsureMethodsLoaded(); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 214 } | 217 } |
| 215 | 218 |
| 216 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) { | 219 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) { |
| 217 // Ignore pressed images, since the standard on Linux is to not highlight | 220 // Ignore pressed images, since the standard on Linux is to not highlight |
| 218 // pressed status icons. | 221 // pressed status icons. |
| 219 } | 222 } |
| 220 | 223 |
| 221 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { | 224 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { |
| 222 DCHECK(!tool_tip_.empty()); | 225 DCHECK(!tool_tip_.empty()); |
| 223 tool_tip_ = base::UTF16ToUTF8(tool_tip); | 226 tool_tip_ = base::UTF16ToUTF8(tool_tip); |
| 224 UpdateClickActionReplacementMenuItem(); | 227 |
| 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 } |
| 225 } | 242 } |
| 226 | 243 |
| 227 void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) { | 244 void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) { |
| 228 if (!g_opened) | 245 if (!g_opened) |
| 229 return; | 246 return; |
| 230 | 247 |
| 248 if (gtk_menu_) { |
| 249 DestroyMenu(); |
| 250 } |
| 231 menu_model_ = model; | 251 menu_model_ = model; |
| 232 | 252 |
| 233 // The icon is created asynchronously so it might not exist when the menu is | 253 // The icon is created asynchronously so it might not exist when the menu is |
| 234 // set. | 254 // set. |
| 235 if (icon_) | 255 if (icon_) |
| 236 SetMenu(); | 256 SetMenu(); |
| 237 } | 257 } |
| 238 | 258 |
| 239 void AppIndicatorIcon::RefreshPlatformContextMenu() { | 259 void AppIndicatorIcon::RefreshPlatformContextMenu() { |
| 240 menu_->Refresh(); | 260 gtk_container_foreach( |
| 261 GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_); |
| 241 } | 262 } |
| 242 | 263 |
| 243 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { | 264 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { |
| 244 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 265 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 245 if (icon_file_path.empty()) | 266 if (icon_file_path.empty()) |
| 246 return; | 267 return; |
| 247 | 268 |
| 248 base::FilePath old_path = icon_file_path_; | 269 base::FilePath old_path = icon_file_path_; |
| 249 icon_file_path_ = icon_file_path; | 270 icon_file_path_ = icon_file_path; |
| 250 | 271 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 266 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon"); | 287 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon"); |
| 267 | 288 |
| 268 // Delete previous icon directory. | 289 // Delete previous icon directory. |
| 269 content::BrowserThread::GetBlockingPool()->PostTask( | 290 content::BrowserThread::GetBlockingPool()->PostTask( |
| 270 FROM_HERE, | 291 FROM_HERE, |
| 271 base::Bind(&DeleteTempImagePath, old_path.DirName())); | 292 base::Bind(&DeleteTempImagePath, old_path.DirName())); |
| 272 } | 293 } |
| 273 } | 294 } |
| 274 | 295 |
| 275 void AppIndicatorIcon::SetMenu() { | 296 void AppIndicatorIcon::SetMenu() { |
| 276 menu_.reset(new AppIndicatorIconMenu(menu_model_)); | 297 gtk_menu_ = gtk_menu_new(); |
| 277 UpdateClickActionReplacementMenuItem(); | 298 |
| 278 app_indicator_set_menu(icon_, menu_->GetGtkMenu()); | 299 if (delegate()->HasClickAction() || menu_model_ == NULL) { |
| 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_)); |
| 279 } | 317 } |
| 280 | 318 |
| 281 void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() { | 319 void AppIndicatorIcon::CreateClickActionReplacement() { |
| 282 // The menu may not have been created yet. | 320 DCHECK(!tool_tip_.empty()); |
| 283 if (!menu_.get()) | |
| 284 return; | |
| 285 | 321 |
| 286 if (!delegate()->HasClickAction() && menu_model_) | 322 // Add "click replacement menu item". |
| 287 return; | 323 GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(tool_tip_.c_str()); |
| 288 | 324 g_object_set_data( |
| 289 DCHECK(!tool_tip_.empty()); | 325 G_OBJECT(menu_item), "click-action-item", GINT_TO_POINTER(1)); |
| 290 menu_->UpdateClickActionReplacementMenuItem( | 326 g_signal_connect(menu_item, "activate", G_CALLBACK(OnClickThunk), this); |
| 291 tool_tip_.c_str(), | 327 gtk_widget_show(menu_item); |
| 292 base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated, | 328 gtk_menu_shell_prepend(GTK_MENU_SHELL(gtk_menu_), menu_item); |
| 293 base::Unretained(this))); | |
| 294 } | 329 } |
| 295 | 330 |
| 296 void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() { | 331 void AppIndicatorIcon::DestroyMenu() { |
| 332 gtk_widget_destroy(gtk_menu_); |
| 333 gtk_menu_ = NULL; |
| 334 menu_model_ = NULL; |
| 335 } |
| 336 |
| 337 void AppIndicatorIcon::OnClick(GtkWidget* menu_item) { |
| 297 if (delegate()) | 338 if (delegate()) |
| 298 delegate()->OnClick(); | 339 delegate()->OnClick(); |
| 299 } | 340 } |
| 300 | 341 |
| 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 |
| 301 } // namespace libgtk2ui | 371 } // namespace libgtk2ui |
| OLD | NEW |