| 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 |