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; | |
pkotwicz
2014/06/02 18:41:58
Creating a temporary directory per icon causes pro
| |
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. | |
pkotwicz
2014/06/02 18:41:58
KDE5 (which uses QIconLoader instead of KIconLoade
| |
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) { | |
pkotwicz
2014/06/02 18:41:58
sni-qt (https://launchpad.net/sni-qt) seems to do
| |
151 // On KDE4, the name of the image file must be unique accross runs of | |
152 // Chrome. | |
Elliot Glaysher
2014/06/02 20:06:04
Is this comment correct? The following seems to ma
| |
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 |