| 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/environment.h" |
| 11 #include "base/file_util.h" | 12 #include "base/file_util.h" |
| 13 #include "base/md5.h" |
| 12 #include "base/memory/ref_counted_memory.h" | 14 #include "base/memory/ref_counted_memory.h" |
| 15 #include "base/nix/xdg_util.h" |
| 13 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 14 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 15 #include "base/threading/sequenced_worker_pool.h" | 18 #include "base/threading/sequenced_worker_pool.h" |
| 16 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h" | 19 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h" |
| 17 #include "content/public/browser/browser_thread.h" | 20 #include "content/public/browser/browser_thread.h" |
| 18 #include "ui/base/models/menu_model.h" | 21 #include "ui/base/models/menu_model.h" |
| 19 #include "ui/gfx/image/image.h" | 22 #include "ui/gfx/image/image.h" |
| 20 #include "ui/gfx/image/image_skia.h" | 23 #include "ui/gfx/image/image_skia.h" |
| 21 | 24 |
| 22 namespace { | 25 namespace { |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 | 117 |
| 115 app_indicator_set_icon_full = | 118 app_indicator_set_icon_full = |
| 116 reinterpret_cast<app_indicator_set_icon_full_func>( | 119 reinterpret_cast<app_indicator_set_icon_full_func>( |
| 117 dlsym(indicator_lib, "app_indicator_set_icon_full")); | 120 dlsym(indicator_lib, "app_indicator_set_icon_full")); |
| 118 | 121 |
| 119 app_indicator_set_icon_theme_path = | 122 app_indicator_set_icon_theme_path = |
| 120 reinterpret_cast<app_indicator_set_icon_theme_path_func>( | 123 reinterpret_cast<app_indicator_set_icon_theme_path_func>( |
| 121 dlsym(indicator_lib, "app_indicator_set_icon_theme_path")); | 124 dlsym(indicator_lib, "app_indicator_set_icon_theme_path")); |
| 122 } | 125 } |
| 123 | 126 |
| 124 base::FilePath CreateTempImageFile(gfx::ImageSkia* image_ptr, | 127 // Returns whether a temporary directory should be created for each app |
| 128 // indicator image. |
| 129 bool ShouldCreateTempDirectoryPerImage(bool using_kde4) { |
| 130 // Create a new temporary directory for each image on Unity since using a |
| 131 // single temporary directory seems to have issues when changing icons in |
| 132 // quick succession. |
| 133 return !using_kde4; |
| 134 } |
| 135 |
| 136 // Returns the subdirectory of |temp_dir| in which the app indicator image |
| 137 // should be saved. |
| 138 base::FilePath GetImageDirectoryPath(bool using_kde4, |
| 139 const base::FilePath& temp_dir) { |
| 140 // On KDE4, an image located in a directory ending with |
| 141 // "icons/hicolor/16x16/apps" can be used as the app indicator image because |
| 142 // "/usr/share/icons/hicolor/16x16/apps" exists. |
| 143 return using_kde4 ? |
| 144 temp_dir.AppendASCII("icons").AppendASCII("hicolor").AppendASCII("16x16"). |
| 145 AppendASCII("apps") : |
| 146 temp_dir; |
| 147 } |
| 148 |
| 149 std::string GetImageFileNameForKDE4( |
| 150 const scoped_refptr<base::RefCountedMemory>& png_data) { |
| 151 // On KDE4, the name of the image file for each different looking bitmap must |
| 152 // be unique. It must also be unique across runs of Chrome. |
| 153 base::MD5Digest digest; |
| 154 base::MD5Sum(png_data->front_as<char>(), png_data->size(), &digest); |
| 155 return base::StringPrintf("chrome_app_indicator_%s.png", |
| 156 base::MD5DigestToBase16(digest).c_str()); |
| 157 } |
| 158 |
| 159 std::string GetImageFileNameForNonKDE4(int icon_change_count, |
| 160 const std::string& id) { |
| 161 return base::StringPrintf("%s_%d.png", id.c_str(), icon_change_count); |
| 162 } |
| 163 |
| 164 // Returns the "icon theme path" given the file path of the app indicator image. |
| 165 std::string GetIconThemePath(bool using_kde4, |
| 166 const base::FilePath& image_path) { |
| 167 return using_kde4 ? |
| 168 image_path.DirName().DirName().DirName().DirName().value() : |
| 169 image_path.DirName().value(); |
| 170 } |
| 171 |
| 172 base::FilePath CreateTempImageFile(bool using_kde4, |
| 173 gfx::ImageSkia* image_ptr, |
| 125 int icon_change_count, | 174 int icon_change_count, |
| 126 std::string id) { | 175 std::string id, |
| 176 const base::FilePath& previous_file_path) { |
| 127 scoped_ptr<gfx::ImageSkia> image(image_ptr); | 177 scoped_ptr<gfx::ImageSkia> image(image_ptr); |
| 128 | 178 |
| 129 scoped_refptr<base::RefCountedMemory> png_data = | 179 scoped_refptr<base::RefCountedMemory> png_data = |
| 130 gfx::Image(*image.get()).As1xPNGBytes(); | 180 gfx::Image(*image.get()).As1xPNGBytes(); |
| 131 if (png_data->size() == 0) { | 181 if (png_data->size() == 0) { |
| 132 // If the bitmap could not be encoded to PNG format, skip it. | 182 // If the bitmap could not be encoded to PNG format, skip it. |
| 133 LOG(WARNING) << "Could not encode icon"; | 183 LOG(WARNING) << "Could not encode icon"; |
| 134 return base::FilePath(); | 184 return base::FilePath(); |
| 135 } | 185 } |
| 136 | 186 |
| 137 base::FilePath temp_dir; | |
| 138 base::FilePath new_file_path; | 187 base::FilePath new_file_path; |
| 188 if (previous_file_path.empty() || |
| 189 ShouldCreateTempDirectoryPerImage(using_kde4)) { |
| 190 base::FilePath tmp_dir; |
| 191 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &tmp_dir)) |
| 192 return base::FilePath(); |
| 193 new_file_path = GetImageDirectoryPath(using_kde4, tmp_dir); |
| 194 if (new_file_path != tmp_dir) { |
| 195 if (!base::CreateDirectory(new_file_path)) |
| 196 return base::FilePath(); |
| 197 } |
| 198 } else { |
| 199 new_file_path = previous_file_path.DirName(); |
| 200 } |
| 139 | 201 |
| 140 // Create a new temporary directory for each image since using a single | 202 new_file_path = new_file_path.Append(using_kde4 ? |
| 141 // temporary directory seems to have issues when changing icons in quick | 203 GetImageFileNameForKDE4(png_data) : |
| 142 // succession. | 204 GetImageFileNameForNonKDE4(icon_change_count, id)); |
| 143 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) | 205 |
| 144 return base::FilePath(); | |
| 145 new_file_path = | |
| 146 temp_dir.Append(id + base::StringPrintf("_%d.png", icon_change_count)); | |
| 147 int bytes_written = | 206 int bytes_written = |
| 148 base::WriteFile(new_file_path, | 207 base::WriteFile(new_file_path, |
| 149 png_data->front_as<char>(), png_data->size()); | 208 png_data->front_as<char>(), png_data->size()); |
| 150 | 209 |
| 151 if (bytes_written != static_cast<int>(png_data->size())) | 210 if (bytes_written != static_cast<int>(png_data->size())) |
| 152 return base::FilePath(); | 211 return base::FilePath(); |
| 153 return new_file_path; | 212 return new_file_path; |
| 154 } | 213 } |
| 155 | 214 |
| 156 void DeleteTempImagePath(const base::FilePath& icon_file_path) { | 215 void DeleteTempDirectory(const base::FilePath& dir_path) { |
| 157 if (icon_file_path.empty()) | 216 if (dir_path.empty()) |
| 158 return; | 217 return; |
| 159 base::DeleteFile(icon_file_path, true); | 218 base::DeleteFile(dir_path, true); |
| 160 } | 219 } |
| 161 | 220 |
| 162 } // namespace | 221 } // namespace |
| 163 | 222 |
| 164 namespace libgtk2ui { | 223 namespace libgtk2ui { |
| 165 | 224 |
| 166 AppIndicatorIcon::AppIndicatorIcon(std::string id, | 225 AppIndicatorIcon::AppIndicatorIcon(std::string id, |
| 167 const gfx::ImageSkia& image, | 226 const gfx::ImageSkia& image, |
| 168 const base::string16& tool_tip) | 227 const base::string16& tool_tip) |
| 169 : id_(id), | 228 : id_(id), |
| 229 using_kde4_(false), |
| 170 icon_(NULL), | 230 icon_(NULL), |
| 171 menu_model_(NULL), | 231 menu_model_(NULL), |
| 172 icon_change_count_(0), | 232 icon_change_count_(0), |
| 173 weak_factory_(this) { | 233 weak_factory_(this) { |
| 234 scoped_ptr<base::Environment> env(base::Environment::Create()); |
| 235 using_kde4_ = base::nix::GetDesktopEnvironment(env.get()) == |
| 236 base::nix::DESKTOP_ENVIRONMENT_KDE4; |
| 237 |
| 174 EnsureMethodsLoaded(); | 238 EnsureMethodsLoaded(); |
| 175 tool_tip_ = base::UTF16ToUTF8(tool_tip); | 239 tool_tip_ = base::UTF16ToUTF8(tool_tip); |
| 176 SetImage(image); | 240 SetImage(image); |
| 177 } | 241 } |
| 178 AppIndicatorIcon::~AppIndicatorIcon() { | 242 AppIndicatorIcon::~AppIndicatorIcon() { |
| 179 if (icon_) { | 243 if (icon_) { |
| 180 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); | 244 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); |
| 181 g_object_unref(icon_); | 245 g_object_unref(icon_); |
| 182 content::BrowserThread::GetBlockingPool()->PostTask( | 246 content::BrowserThread::GetBlockingPool()->PostTask( |
| 183 FROM_HERE, | 247 FROM_HERE, |
| 184 base::Bind(&DeleteTempImagePath, icon_file_path_.DirName())); | 248 base::Bind(&DeleteTempDirectory, icon_file_path_.DirName())); |
| 185 } | 249 } |
| 186 } | 250 } |
| 187 | 251 |
| 188 // static | 252 // static |
| 189 bool AppIndicatorIcon::CouldOpen() { | 253 bool AppIndicatorIcon::CouldOpen() { |
| 190 EnsureMethodsLoaded(); | 254 EnsureMethodsLoaded(); |
| 191 return g_opened; | 255 return g_opened; |
| 192 } | 256 } |
| 193 | 257 |
| 194 void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) { | 258 void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) { |
| 195 if (!g_opened) | 259 if (!g_opened) |
| 196 return; | 260 return; |
| 197 | 261 |
| 198 ++icon_change_count_; | 262 ++icon_change_count_; |
| 199 | 263 |
| 200 // We create a deep copy of the image since it may have been freed by the time | 264 // We create a deep copy of the image since it may have been freed by the time |
| 201 // it's accessed in the other thread. | 265 // it's accessed in the other thread. |
| 202 scoped_ptr<gfx::ImageSkia> safe_image(image.DeepCopy()); | 266 scoped_ptr<gfx::ImageSkia> safe_image(image.DeepCopy()); |
| 203 base::PostTaskAndReplyWithResult( | 267 base::PostTaskAndReplyWithResult( |
| 204 content::BrowserThread::GetBlockingPool() | 268 content::BrowserThread::GetBlockingPool() |
| 205 ->GetTaskRunnerWithShutdownBehavior( | 269 ->GetTaskRunnerWithShutdownBehavior( |
| 206 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN).get(), | 270 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN).get(), |
| 207 FROM_HERE, | 271 FROM_HERE, |
| 208 base::Bind(&CreateTempImageFile, | 272 base::Bind(&CreateTempImageFile, |
| 273 using_kde4_, |
| 209 safe_image.release(), | 274 safe_image.release(), |
| 210 icon_change_count_, | 275 icon_change_count_, |
| 211 id_), | 276 id_, |
| 277 icon_file_path_), |
| 212 base::Bind(&AppIndicatorIcon::SetImageFromFile, | 278 base::Bind(&AppIndicatorIcon::SetImageFromFile, |
| 213 weak_factory_.GetWeakPtr())); | 279 weak_factory_.GetWeakPtr())); |
| 214 } | 280 } |
| 215 | 281 |
| 216 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) { | 282 void AppIndicatorIcon::SetPressedImage(const gfx::ImageSkia& image) { |
| 217 // Ignore pressed images, since the standard on Linux is to not highlight | 283 // Ignore pressed images, since the standard on Linux is to not highlight |
| 218 // pressed status icons. | 284 // pressed status icons. |
| 219 } | 285 } |
| 220 | 286 |
| 221 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { | 287 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 243 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { | 309 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { |
| 244 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 310 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 245 if (icon_file_path.empty()) | 311 if (icon_file_path.empty()) |
| 246 return; | 312 return; |
| 247 | 313 |
| 248 base::FilePath old_path = icon_file_path_; | 314 base::FilePath old_path = icon_file_path_; |
| 249 icon_file_path_ = icon_file_path; | 315 icon_file_path_ = icon_file_path; |
| 250 | 316 |
| 251 std::string icon_name = | 317 std::string icon_name = |
| 252 icon_file_path_.BaseName().RemoveExtension().value(); | 318 icon_file_path_.BaseName().RemoveExtension().value(); |
| 253 std::string icon_dir = icon_file_path_.DirName().value(); | 319 std::string icon_dir = GetIconThemePath(using_kde4_, icon_file_path); |
| 254 if (!icon_) { | 320 if (!icon_) { |
| 255 icon_ = | 321 icon_ = |
| 256 app_indicator_new_with_path(id_.c_str(), | 322 app_indicator_new_with_path(id_.c_str(), |
| 257 icon_name.c_str(), | 323 icon_name.c_str(), |
| 258 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, | 324 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, |
| 259 icon_dir.c_str()); | 325 icon_dir.c_str()); |
| 260 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE); | 326 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE); |
| 261 SetMenu(); | 327 SetMenu(); |
| 262 } else { | 328 } else { |
| 263 // Currently we are creating a new temp directory every time the icon is | 329 // Currently we are creating a new temp directory every time the icon is |
| 264 // set. So we need to set the directory each time. | 330 // set. So we need to set the directory each time. |
| 265 app_indicator_set_icon_theme_path(icon_, icon_dir.c_str()); | 331 app_indicator_set_icon_theme_path(icon_, icon_dir.c_str()); |
| 266 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon"); | 332 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon"); |
| 267 | 333 |
| 268 // Delete previous icon directory. | 334 if (ShouldCreateTempDirectoryPerImage(using_kde4_)) { |
| 269 content::BrowserThread::GetBlockingPool()->PostTask( | 335 // Delete previous icon directory. |
| 270 FROM_HERE, | 336 content::BrowserThread::GetBlockingPool()->PostTask( |
| 271 base::Bind(&DeleteTempImagePath, old_path.DirName())); | 337 FROM_HERE, |
| 338 base::Bind(&DeleteTempDirectory, old_path.DirName())); |
| 339 } |
| 272 } | 340 } |
| 273 } | 341 } |
| 274 | 342 |
| 275 void AppIndicatorIcon::SetMenu() { | 343 void AppIndicatorIcon::SetMenu() { |
| 276 menu_.reset(new AppIndicatorIconMenu(menu_model_)); | 344 menu_.reset(new AppIndicatorIconMenu(menu_model_)); |
| 277 UpdateClickActionReplacementMenuItem(); | 345 UpdateClickActionReplacementMenuItem(); |
| 278 app_indicator_set_menu(icon_, menu_->GetGtkMenu()); | 346 app_indicator_set_menu(icon_, menu_->GetGtkMenu()); |
| 279 } | 347 } |
| 280 | 348 |
| 281 void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() { | 349 void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 292 base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated, | 360 base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated, |
| 293 base::Unretained(this))); | 361 base::Unretained(this))); |
| 294 } | 362 } |
| 295 | 363 |
| 296 void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() { | 364 void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() { |
| 297 if (delegate()) | 365 if (delegate()) |
| 298 delegate()->OnClick(); | 366 delegate()->OnClick(); |
| 299 } | 367 } |
| 300 | 368 |
| 301 } // namespace libgtk2ui | 369 } // namespace libgtk2ui |
| OLD | NEW |