Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(737)

Side by Side Diff: chrome/browser/ui/libgtk2ui/app_indicator_icon.cc

Issue 291893009: Fall back to X11 system tray icons if libappindicator is not available (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698