task_manager_gtk.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/gtk/task_manager_gtk.h" 6 7#include <gdk/gdkkeysyms.h> 8 9#include <algorithm> 10#include <set> 11#include <utility> 12#include <vector> 13 14#include "base/auto_reset.h" 15#include "base/command_line.h" 16#include "base/logging.h" 17#include "base/utf_string_conversions.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/defaults.h" 20#include "chrome/browser/memory_purger.h" 21#include "chrome/browser/prefs/pref_service.h" 22#include "chrome/browser/prefs/scoped_user_pref_update.h" 23#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" 24#include "chrome/browser/ui/gtk/gtk_theme_service.h" 25#include "chrome/browser/ui/gtk/gtk_theme_service.h" 26#include "chrome/browser/ui/gtk/gtk_tree.h" 27#include "chrome/browser/ui/gtk/gtk_util.h" 28#include "chrome/browser/ui/gtk/menu_gtk.h" 29#include "chrome/common/chrome_switches.h" 30#include "chrome/common/pref_names.h" 31#include "grit/chromium_strings.h" 32#include "grit/ui_resources.h" 33#include "third_party/skia/include/core/SkBitmap.h" 34#include "ui/base/gtk/gtk_hig_constants.h" 35#include "ui/base/gtk/menu_label_accelerator_util.h" 36#include "ui/base/l10n/l10n_util.h" 37#include "ui/base/models/simple_menu_model.h" 38#include "ui/base/resource/resource_bundle.h" 39#include "ui/gfx/gtk_util.h" 40#include "ui/gfx/image/image.h" 41 42namespace { 43 44// The task manager window default size. 45const int kDefaultWidth = 460; 46const int kDefaultHeight = 270; 47 48// The resource id for the 'End process' button. 49const gint kTaskManagerResponseKill = 1; 50 51// The resource id for the 'Stats for nerds' link button. 52const gint kTaskManagerAboutMemoryLink = 2; 53 54// The resource id for the 'Purge Memory' button 55const gint kTaskManagerPurgeMemory = 3; 56 57enum TaskManagerColumn { 58 kTaskManagerIcon, 59 kTaskManagerTask, 60 kTaskManagerProfileName, 61 kTaskManagerSharedMem, 62 kTaskManagerPrivateMem, 63 kTaskManagerCPU, 64 kTaskManagerNetwork, 65 kTaskManagerProcessID, 66 kTaskManagerJavaScriptMemory, 67 kTaskManagerWebCoreImageCache, 68 kTaskManagerWebCoreScriptsCache, 69 kTaskManagerWebCoreCssCache, 70 kTaskManagerVideoMemory, 71 kTaskManagerFPS, 72 kTaskManagerSqliteMemoryUsed, 73 kTaskManagerGoatsTeleported, 74 // Columns below this point are not visible in the task manager. 75 kTaskManagerBackgroundColor, 76 kTaskManagerColumnCount, 77}; 78 79const TaskManagerColumn kTaskManagerLastVisibleColumn = 80 kTaskManagerGoatsTeleported; 81 82static const GdkColor kHighlightColor = GDK_COLOR_RGB(0xff, 0xfa, 0xcd); 83 84TaskManagerColumn TaskManagerResourceIDToColumnID(int id) { 85 switch (id) { 86 case IDS_TASK_MANAGER_TASK_COLUMN: 87 return kTaskManagerTask; 88 case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN: 89 return kTaskManagerProfileName; 90 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: 91 return kTaskManagerSharedMem; 92 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: 93 return kTaskManagerPrivateMem; 94 case IDS_TASK_MANAGER_CPU_COLUMN: 95 return kTaskManagerCPU; 96 case IDS_TASK_MANAGER_NET_COLUMN: 97 return kTaskManagerNetwork; 98 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: 99 return kTaskManagerProcessID; 100 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 101 return kTaskManagerJavaScriptMemory; 102 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 103 return kTaskManagerWebCoreImageCache; 104 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 105 return kTaskManagerWebCoreScriptsCache; 106 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 107 return kTaskManagerWebCoreCssCache; 108 case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN: 109 return kTaskManagerVideoMemory; 110 case IDS_TASK_MANAGER_FPS_COLUMN: 111 return kTaskManagerFPS; 112 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN: 113 return kTaskManagerSqliteMemoryUsed; 114 case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN: 115 return kTaskManagerGoatsTeleported; 116 default: 117 NOTREACHED(); 118 return static_cast<TaskManagerColumn>(-1); 119 } 120} 121 122int TaskManagerColumnIDToResourceID(int id) { 123 switch (id) { 124 case kTaskManagerTask: 125 return IDS_TASK_MANAGER_TASK_COLUMN; 126 case kTaskManagerProfileName: 127 return IDS_TASK_MANAGER_PROFILE_NAME_COLUMN; 128 case kTaskManagerSharedMem: 129 return IDS_TASK_MANAGER_SHARED_MEM_COLUMN; 130 case kTaskManagerPrivateMem: 131 return IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN; 132 case kTaskManagerCPU: 133 return IDS_TASK_MANAGER_CPU_COLUMN; 134 case kTaskManagerNetwork: 135 return IDS_TASK_MANAGER_NET_COLUMN; 136 case kTaskManagerProcessID: 137 return IDS_TASK_MANAGER_PROCESS_ID_COLUMN; 138 case kTaskManagerJavaScriptMemory: 139 return IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN; 140 case kTaskManagerWebCoreImageCache: 141 return IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN; 142 case kTaskManagerWebCoreScriptsCache: 143 return IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN; 144 case kTaskManagerWebCoreCssCache: 145 return IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN; 146 case kTaskManagerVideoMemory: 147 return IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN; 148 case kTaskManagerFPS: 149 return IDS_TASK_MANAGER_FPS_COLUMN; 150 case kTaskManagerSqliteMemoryUsed: 151 return IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN; 152 case kTaskManagerGoatsTeleported: 153 return IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN; 154 default: 155 NOTREACHED(); 156 return -1; 157 } 158} 159 160// Should be used for all gtk_tree_view functions that require a column index on 161// input. 162// 163// We need colid - 1 because the gtk_tree_view function is asking for the 164// column index, not the column id, and both kTaskManagerIcon and 165// kTaskManagerTask are in the same column index, so all column IDs are off by 166// one. 167int TreeViewColumnIndexFromID(TaskManagerColumn colid) { 168 return colid - 1; 169} 170 171// Shows or hides a treeview column. 172void TreeViewColumnSetVisible(GtkWidget* treeview, TaskManagerColumn colid, 173 bool visible) { 174 GtkTreeViewColumn* column = gtk_tree_view_get_column( 175 GTK_TREE_VIEW(treeview), TreeViewColumnIndexFromID(colid)); 176 gtk_tree_view_column_set_visible(column, visible); 177} 178 179bool TreeViewColumnIsVisible(GtkWidget* treeview, TaskManagerColumn colid) { 180 GtkTreeViewColumn* column = gtk_tree_view_get_column( 181 GTK_TREE_VIEW(treeview), TreeViewColumnIndexFromID(colid)); 182 return gtk_tree_view_column_get_visible(column); 183} 184 185// The task column is special because it has an icon and it gets special 186// treatment with respect to resizing the columns. 187void TreeViewInsertTaskColumn(GtkWidget* treeview, int resid) { 188 int colid = TaskManagerResourceIDToColumnID(resid); 189 GtkTreeViewColumn* column = gtk_tree_view_column_new(); 190 gtk_tree_view_column_set_title(column, 191 l10n_util::GetStringUTF8(resid).c_str()); 192 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(treeview), colid); 193 GtkCellRenderer* image_renderer = gtk_cell_renderer_pixbuf_new(); 194 gtk_tree_view_column_pack_start(column, image_renderer, FALSE); 195 gtk_tree_view_column_add_attribute(column, image_renderer, 196 "pixbuf", kTaskManagerIcon); 197 gtk_tree_view_column_add_attribute(column, image_renderer, 198 "cell-background-gdk", 199 kTaskManagerBackgroundColor); 200 GtkCellRenderer* text_renderer = gtk_cell_renderer_text_new(); 201 gtk_tree_view_column_pack_start(column, text_renderer, TRUE); 202 gtk_tree_view_column_add_attribute(column, text_renderer, "markup", colid); 203 gtk_tree_view_column_add_attribute(column, text_renderer, 204 "cell-background-gdk", 205 kTaskManagerBackgroundColor); 206 gtk_tree_view_column_set_resizable(column, TRUE); 207 // This is temporary: we'll turn expanding off after getting the size. 208 gtk_tree_view_column_set_expand(column, TRUE); 209 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); 210 gtk_tree_view_column_set_sort_column_id(column, colid); 211} 212 213// Inserts a column with a column id of |colid| and |name|. 214void TreeViewInsertColumnWithName(GtkWidget* treeview, 215 TaskManagerColumn colid, const char* name) { 216 GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); 217 gtk_tree_view_insert_column_with_attributes( 218 GTK_TREE_VIEW(treeview), -1, 219 name, renderer, 220 "text", colid, 221 "cell-background-gdk", kTaskManagerBackgroundColor, 222 NULL); 223 GtkTreeViewColumn* column = gtk_tree_view_get_column( 224 GTK_TREE_VIEW(treeview), TreeViewColumnIndexFromID(colid)); 225 gtk_tree_view_column_set_resizable(column, TRUE); 226 gtk_tree_view_column_set_sort_column_id(column, colid); 227} 228 229// Loads the column name from |resid| and uses the corresponding 230// TaskManagerColumn value as the column id to insert into the treeview. 231void TreeViewInsertColumn(GtkWidget* treeview, int resid) { 232 TreeViewInsertColumnWithName(treeview, TaskManagerResourceIDToColumnID(resid), 233 l10n_util::GetStringUTF8(resid).c_str()); 234} 235 236// Set the current width of the column without forcing a fixed or maximum 237// width as gtk_tree_view_column_set_[fixed|maximum]_width() would. This would 238// basically be gtk_tree_view_column_set_width() except that there is no such 239// function. It turns out that other applications have done similar hacks to do 240// the same thing - search the web for that nonexistent function name! :) 241void TreeViewColumnSetWidth(GtkTreeViewColumn* column, gint width) { 242 column->width = width; 243 column->resized_width = width; 244 column->use_resized_width = TRUE; 245 // Needed for use_resized_width to be effective. 246 gtk_widget_queue_resize(column->tree_view); 247} 248 249} // namespace 250 251class TaskManagerGtk::ContextMenuController 252 : public ui::SimpleMenuModel::Delegate { 253 public: 254 explicit ContextMenuController(TaskManagerGtk* task_manager) 255 : task_manager_(task_manager) { 256 menu_model_.reset(new ui::SimpleMenuModel(this)); 257 for (int i = kTaskManagerTask; i <= kTaskManagerLastVisibleColumn; i++) { 258 menu_model_->AddCheckItemWithStringId( 259 i, TaskManagerColumnIDToResourceID(i)); 260 } 261 menu_.reset(new MenuGtk(NULL, menu_model_.get())); 262 } 263 264 virtual ~ContextMenuController() {} 265 266 void RunMenu(const gfx::Point& point, guint32 event_time) { 267 menu_->PopupAsContext(point, event_time); 268 } 269 270 void Cancel() { 271 task_manager_ = NULL; 272 menu_->Cancel(); 273 } 274 275 private: 276 // ui::SimpleMenuModel::Delegate implementation: 277 virtual bool IsCommandIdEnabled(int command_id) const { 278 if (!task_manager_) 279 return false; 280 281 return true; 282 } 283 284 virtual bool IsCommandIdChecked(int command_id) const { 285 if (!task_manager_) 286 return false; 287 288 TaskManagerColumn colid = static_cast<TaskManagerColumn>(command_id); 289 return TreeViewColumnIsVisible(task_manager_->treeview_, colid); 290 } 291 292 virtual bool GetAcceleratorForCommandId( 293 int command_id, 294 ui::Accelerator* accelerator) { 295 return false; 296 } 297 298 virtual void ExecuteCommand(int command_id) { 299 if (!task_manager_) 300 return; 301 302 TaskManagerColumn colid = static_cast<TaskManagerColumn>(command_id); 303 bool visible = !TreeViewColumnIsVisible(task_manager_->treeview_, colid); 304 TreeViewColumnSetVisible(task_manager_->treeview_, colid, visible); 305 } 306 307 // The model and view for the right click context menu. 308 scoped_ptr<ui::SimpleMenuModel> menu_model_; 309 scoped_ptr<MenuGtk> menu_; 310 311 // The TaskManager the context menu was brought up for. Set to NULL when the 312 // menu is canceled. 313 TaskManagerGtk* task_manager_; 314 315 DISALLOW_COPY_AND_ASSIGN(ContextMenuController); 316}; 317 318TaskManagerGtk::TaskManagerGtk(bool highlight_background_resources) 319 : task_manager_(TaskManager::GetInstance()), 320 model_(TaskManager::GetInstance()->model()), 321 dialog_(NULL), 322 treeview_(NULL), 323 process_list_(NULL), 324 process_count_(0), 325 ignore_selection_changed_(false), 326 highlight_background_resources_(highlight_background_resources) { 327 Init(); 328} 329 330// static 331TaskManagerGtk* TaskManagerGtk::instance_ = NULL; 332 333TaskManagerGtk::~TaskManagerGtk() { 334 model_->RemoveObserver(this); 335 task_manager_->OnWindowClosed(); 336 337 gtk_accel_group_disconnect_key(accel_group_, GDK_w, GDK_CONTROL_MASK); 338 gtk_window_remove_accel_group(GTK_WINDOW(dialog_), accel_group_); 339 g_object_unref(accel_group_); 340 accel_group_ = NULL; 341 342 // Disconnect the destroy signal so it doesn't delete |this|. 343 g_signal_handler_disconnect(G_OBJECT(dialog_), destroy_handler_id_); 344 gtk_widget_destroy(dialog_); 345} 346 347//////////////////////////////////////////////////////////////////////////////// 348// TaskManagerGtk, TaskManagerModelObserver implementation: 349 350void TaskManagerGtk::OnModelChanged() { 351 // Nothing to do. 352} 353 354void TaskManagerGtk::OnItemsChanged(int start, int length) { 355 GtkTreeIter iter; 356 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_), &iter, 357 NULL, start)) { 358 NOTREACHED() << "Can't get child " << start << 359 " from GTK_TREE_MODEL(process_list_)"; 360 } 361 362 for (int i = start; i < start + length; i++) { 363 SetRowDataFromModel(i, &iter); 364 if (i != start + length - 1) { 365 if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(process_list_), &iter)) { 366 NOTREACHED() << "Can't get next GtkTreeIter object from process_list_ " 367 "iterator at position " << i; 368 } 369 } 370 } 371} 372 373void TaskManagerGtk::OnItemsAdded(int start, int length) { 374 AutoReset<bool> autoreset(&ignore_selection_changed_, true); 375 376 GtkTreeIter iter; 377 if (start == 0) { 378 gtk_list_store_prepend(process_list_, &iter); 379 } else if (start >= process_count_) { 380 gtk_list_store_append(process_list_, &iter); 381 } else { 382 GtkTreeIter sibling; 383 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_), &sibling, 384 NULL, start); 385 gtk_list_store_insert_before(process_list_, &iter, &sibling); 386 } 387 388 SetRowDataFromModel(start, &iter); 389 390 for (int i = start + 1; i < start + length; i++) { 391 gtk_list_store_insert_after(process_list_, &iter, &iter); 392 SetRowDataFromModel(i, &iter); 393 } 394 395 process_count_ += length; 396} 397 398void TaskManagerGtk::OnItemsRemoved(int start, int length) { 399 { 400 AutoReset<bool> autoreset(&ignore_selection_changed_, true); 401 402 GtkTreeIter iter; 403 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(process_list_), &iter, 404 NULL, start); 405 406 for (int i = 0; i < length; i++) { 407 // |iter| is moved to the next valid node when the current node is 408 // removed. 409 gtk_list_store_remove(process_list_, &iter); 410 } 411 412 process_count_ -= length; 413 } 414 415 // It is possible that we have removed the current selection; run selection 416 // changed to detect that case. 417 OnSelectionChanged(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview_))); 418} 419 420//////////////////////////////////////////////////////////////////////////////// 421// TaskManagerGtk, public: 422 423void TaskManagerGtk::Close() { 424 // Blow away our dialog - this will cause TaskManagerGtk to free itself. 425 gtk_widget_destroy(dialog_); 426 DCHECK(!instance_); 427} 428 429// static 430void TaskManagerGtk::Show(bool highlight_background_resources) { 431 if (instance_ && 432 instance_->highlight_background_resources_ != 433 highlight_background_resources) { 434 instance_->Close(); 435 DCHECK(!instance_); 436 } 437 438 if (instance_) { 439 // If there's a Task manager window open already, just activate it. 440 gtk_util::PresentWindow(instance_->dialog_, 0); 441 } else { 442 instance_ = new TaskManagerGtk(highlight_background_resources); 443 instance_->model_->StartUpdating(); 444 } 445} 446 447//////////////////////////////////////////////////////////////////////////////// 448// TaskManagerGtk, private: 449 450void TaskManagerGtk::Init() { 451 dialog_ = gtk_dialog_new_with_buttons( 452 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_TITLE).c_str(), 453 // Task Manager window is shared between all browsers. 454 NULL, 455 GTK_DIALOG_NO_SEPARATOR, 456 NULL); 457 458 // Allow browser windows to go in front of the task manager dialog in 459 // metacity. 460 gtk_window_set_type_hint(GTK_WINDOW(dialog_), GDK_WINDOW_TYPE_HINT_NORMAL); 461 462 if (CommandLine::ForCurrentProcess()->HasSwitch( 463 switches::kPurgeMemoryButton)) { 464 gtk_dialog_add_button(GTK_DIALOG(dialog_), 465 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_PURGE_MEMORY).c_str(), 466 kTaskManagerPurgeMemory); 467 } 468 469 if (browser_defaults::kShowCancelButtonInTaskManager) { 470 gtk_dialog_add_button(GTK_DIALOG(dialog_), 471 l10n_util::GetStringUTF8(IDS_CLOSE).c_str(), 472 GTK_RESPONSE_DELETE_EVENT); 473 } 474 475 gtk_dialog_add_button(GTK_DIALOG(dialog_), 476 ui::ConvertAcceleratorsFromWindowsStyle( 477 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_KILL)).c_str(), 478 kTaskManagerResponseKill); 479 480 // The response button should not be sensitive when the dialog is first opened 481 // because the selection is initially empty. 482 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_), 483 kTaskManagerResponseKill, FALSE); 484 485 GtkWidget* link = gtk_chrome_link_button_new( 486 l10n_util::GetStringUTF8(IDS_TASK_MANAGER_ABOUT_MEMORY_LINK).c_str()); 487 gtk_dialog_add_action_widget(GTK_DIALOG(dialog_), link, 488 kTaskManagerAboutMemoryLink); 489 490 // Setting the link widget to secondary positions the button on the left side 491 // of the action area (vice versa for RTL layout). 492 GtkWidget* action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog_)); 493 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(action_area), link, TRUE); 494 495 ConnectAccelerators(); 496 497 GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog_)); 498 gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing); 499 500 destroy_handler_id_ = g_signal_connect(dialog_, "destroy", 501 G_CALLBACK(OnDestroyThunk), this); 502 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); 503 g_signal_connect(dialog_, "button-press-event", 504 G_CALLBACK(OnButtonEventThunk), this); 505 gtk_widget_add_events(dialog_, 506 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); 507 508 // Wrap the treeview widget in a scrolled window in order to have a frame. 509 GtkWidget* scrolled = gtk_scrolled_window_new(NULL, NULL); 510 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), 511 GTK_SHADOW_ETCHED_IN); 512 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), 513 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 514 515 gtk_container_add(GTK_CONTAINER(content_area), scrolled); 516 517 CreateTaskManagerTreeview(); 518 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview_), TRUE); 519 g_signal_connect(treeview_, "row-activated", 520 G_CALLBACK(OnRowActivatedThunk), this); 521 g_signal_connect(treeview_, "button-press-event", 522 G_CALLBACK(OnButtonEventThunk), this); 523 524 // |selection| is owned by |treeview_|. 525 GtkTreeSelection* selection = gtk_tree_view_get_selection( 526 GTK_TREE_VIEW(treeview_)); 527 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); 528 g_signal_connect(selection, "changed", 529 G_CALLBACK(OnSelectionChangedThunk), this); 530 531 gtk_container_add(GTK_CONTAINER(scrolled), treeview_); 532 533 SetInitialDialogSize(); 534 gtk_util::ShowDialog(dialog_); 535 536 // If the model already has resources, we need to add them before we start 537 // observing events. 538 if (model_->ResourceCount() > 0) 539 OnItemsAdded(0, model_->ResourceCount()); 540 541 model_->AddObserver(this); 542} 543 544void TaskManagerGtk::SetInitialDialogSize() { 545 // Hook up to the realize event so we can size the task column to the 546 // size of the leftover space after packing the other columns. 547 g_signal_connect(treeview_, "realize", 548 G_CALLBACK(OnTreeViewRealizeThunk), this); 549 // If we previously saved the dialog's bounds, use them. 550 if (g_browser_process->local_state()) { 551 const DictionaryValue* placement_pref = 552 g_browser_process->local_state()->GetDictionary( 553 prefs::kTaskManagerWindowPlacement); 554 int top = 0, left = 0, bottom = 1, right = 1; 555 if (placement_pref && 556 placement_pref->GetInteger("top", &top) && 557 placement_pref->GetInteger("left", &left) && 558 placement_pref->GetInteger("bottom", &bottom) && 559 placement_pref->GetInteger("right", &right)) { 560 gtk_window_resize(GTK_WINDOW(dialog_), 561 std::max(1, right - left), 562 std::max(1, bottom - top)); 563 return; 564 } 565 } 566 567 // Otherwise, just set a default size (GTK will override this if it's not 568 // large enough to hold the window's contents). 569 gtk_window_set_default_size( 570 GTK_WINDOW(dialog_), kDefaultWidth, kDefaultHeight); 571} 572 573void TaskManagerGtk::ConnectAccelerators() { 574 accel_group_ = gtk_accel_group_new(); 575 gtk_window_add_accel_group(GTK_WINDOW(dialog_), accel_group_); 576 577 gtk_accel_group_connect(accel_group_, 578 GDK_w, GDK_CONTROL_MASK, GtkAccelFlags(0), 579 g_cclosure_new(G_CALLBACK(OnGtkAcceleratorThunk), 580 this, NULL)); 581} 582 583void TaskManagerGtk::CreateTaskManagerTreeview() { 584 process_list_ = gtk_list_store_new(kTaskManagerColumnCount, 585 GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, 586 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, 587 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, 588 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_COLOR); 589 590 // Support sorting on all columns. 591 process_list_sort_ = gtk_tree_model_sort_new_with_model( 592 GTK_TREE_MODEL(process_list_)); 593 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 594 kTaskManagerTask, 595 ComparePage, this, NULL); 596 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 597 kTaskManagerTask, 598 CompareProfileName, this, NULL); 599 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 600 kTaskManagerSharedMem, 601 CompareSharedMemory, this, NULL); 602 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 603 kTaskManagerPrivateMem, 604 ComparePrivateMemory, this, NULL); 605 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 606 kTaskManagerJavaScriptMemory, 607 CompareV8Memory, this, NULL); 608 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 609 kTaskManagerCPU, 610 CompareCPU, this, NULL); 611 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 612 kTaskManagerNetwork, 613 CompareNetwork, this, NULL); 614 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 615 kTaskManagerProcessID, 616 CompareProcessID, this, NULL); 617 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 618 kTaskManagerWebCoreImageCache, 619 CompareWebCoreImageCache, this, NULL); 620 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 621 kTaskManagerWebCoreScriptsCache, 622 CompareWebCoreScriptsCache, this, NULL); 623 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 624 kTaskManagerWebCoreCssCache, 625 CompareWebCoreCssCache, this, NULL); 626 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 627 kTaskManagerVideoMemory, 628 CompareVideoMemory, this, NULL); 629 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 630 kTaskManagerFPS, 631 CompareFPS, this, NULL); 632 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 633 kTaskManagerSqliteMemoryUsed, 634 CompareSqliteMemoryUsed, this, NULL); 635 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(process_list_sort_), 636 kTaskManagerGoatsTeleported, 637 CompareGoatsTeleported, this, NULL); 638 treeview_ = gtk_tree_view_new_with_model(process_list_sort_); 639 640 // Insert all the columns. 641 TreeViewInsertTaskColumn(treeview_, IDS_TASK_MANAGER_TASK_COLUMN); 642 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_PROFILE_NAME_COLUMN); 643 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_SHARED_MEM_COLUMN); 644 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN); 645 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_CPU_COLUMN); 646 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_NET_COLUMN); 647 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_PROCESS_ID_COLUMN); 648 TreeViewInsertColumn(treeview_, 649 IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN); 650 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN); 651 TreeViewInsertColumn(treeview_, 652 IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN); 653 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN); 654 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN); 655 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_FPS_COLUMN); 656 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN); 657 TreeViewInsertColumn(treeview_, IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN); 658 659 // Hide some columns by default. 660 TreeViewColumnSetVisible(treeview_, kTaskManagerProfileName, false); 661 TreeViewColumnSetVisible(treeview_, kTaskManagerSharedMem, false); 662 TreeViewColumnSetVisible(treeview_, kTaskManagerProcessID, false); 663 TreeViewColumnSetVisible(treeview_, kTaskManagerJavaScriptMemory, false); 664 TreeViewColumnSetVisible(treeview_, kTaskManagerWebCoreImageCache, false); 665 TreeViewColumnSetVisible(treeview_, kTaskManagerWebCoreScriptsCache, false); 666 TreeViewColumnSetVisible(treeview_, kTaskManagerWebCoreCssCache, false); 667 TreeViewColumnSetVisible(treeview_, kTaskManagerVideoMemory, false); 668 TreeViewColumnSetVisible(treeview_, kTaskManagerSqliteMemoryUsed, false); 669 TreeViewColumnSetVisible(treeview_, kTaskManagerGoatsTeleported, false); 670 671 g_object_unref(process_list_); 672 g_object_unref(process_list_sort_); 673} 674 675bool IsSharedByGroup(int col_id) { 676 switch (col_id) { 677 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: 678 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: 679 case IDS_TASK_MANAGER_CPU_COLUMN: 680 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: 681 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 682 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 683 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 684 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 685 return true; 686 default: 687 return false; 688 } 689} 690 691std::string TaskManagerGtk::GetModelText(int row, int col_id) { 692 if (IsSharedByGroup(col_id) && !model_->IsResourceFirstInGroup(row)) 693 return std::string(); 694 695 switch (col_id) { 696 case IDS_TASK_MANAGER_TASK_COLUMN: // Process 697 return UTF16ToUTF8(model_->GetResourceTitle(row)); 698 699 case IDS_TASK_MANAGER_PROFILE_NAME_COLUMN: // Profile name 700 return UTF16ToUTF8(model_->GetResourceProfileName(row)); 701 702 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: // Memory 703 return UTF16ToUTF8(model_->GetResourcePrivateMemory(row)); 704 705 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: // Memory 706 return UTF16ToUTF8(model_->GetResourceSharedMemory(row)); 707 708 case IDS_TASK_MANAGER_CPU_COLUMN: // CPU 709 return UTF16ToUTF8(model_->GetResourceCPUUsage(row)); 710 711 case IDS_TASK_MANAGER_NET_COLUMN: // Net 712 return UTF16ToUTF8(model_->GetResourceNetworkUsage(row)); 713 714 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: // Process ID 715 return UTF16ToUTF8(model_->GetResourceProcessId(row)); 716 717 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 718 return UTF16ToUTF8(model_->GetResourceV8MemoryAllocatedSize(row)); 719 720 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 721 return UTF16ToUTF8(model_->GetResourceWebCoreImageCacheSize(row)); 722 723 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 724 return UTF16ToUTF8(model_->GetResourceWebCoreScriptsCacheSize(row)); 725 726 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 727 return UTF16ToUTF8(model_->GetResourceWebCoreCSSCacheSize(row)); 728 729 case IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN: 730 return UTF16ToUTF8(model_->GetResourceVideoMemory(row)); 731 732 case IDS_TASK_MANAGER_FPS_COLUMN: 733 return UTF16ToUTF8(model_->GetResourceFPS(row)); 734 735 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN: 736 return UTF16ToUTF8(model_->GetResourceSqliteMemoryUsed(row)); 737 738 case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN: // Goats Teleported! 739 return UTF16ToUTF8(model_->GetResourceGoatsTeleported(row)); 740 741 default: 742 NOTREACHED(); 743 return std::string(); 744 } 745} 746 747GdkPixbuf* TaskManagerGtk::GetModelIcon(int row) { 748 SkBitmap icon = *model_->GetResourceIcon(row).bitmap(); 749 if (icon.pixelRef() == 750 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 751 IDR_DEFAULT_FAVICON).AsBitmap().pixelRef()) { 752 return static_cast<GdkPixbuf*>(g_object_ref( 753 GtkThemeService::GetDefaultFavicon(true).ToGdkPixbuf())); 754 } 755 756 return gfx::GdkPixbufFromSkBitmap(icon); 757} 758 759void TaskManagerGtk::SetRowDataFromModel(int row, GtkTreeIter* iter) { 760 GdkPixbuf* icon = GetModelIcon(row); 761 std::string task = GetModelText(row, IDS_TASK_MANAGER_TASK_COLUMN); 762 std::string profile_name = 763 GetModelText(row, IDS_TASK_MANAGER_PROFILE_NAME_COLUMN); 764 gchar* task_markup = g_markup_escape_text(task.c_str(), task.length()); 765 std::string shared_mem = 766 GetModelText(row, IDS_TASK_MANAGER_SHARED_MEM_COLUMN); 767 std::string priv_mem = GetModelText(row, IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN); 768 std::string cpu = GetModelText(row, IDS_TASK_MANAGER_CPU_COLUMN); 769 std::string net = GetModelText(row, IDS_TASK_MANAGER_NET_COLUMN); 770 std::string procid = GetModelText(row, IDS_TASK_MANAGER_PROCESS_ID_COLUMN); 771 772 // Querying the renderer metrics is slow as it has to do IPC, so only do it 773 // when the columns are visible. 774 std::string javascript_memory; 775 if (TreeViewColumnIsVisible(treeview_, kTaskManagerJavaScriptMemory)) { 776 javascript_memory = 777 GetModelText(row, IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN); 778 } 779 std::string wk_img_cache; 780 if (TreeViewColumnIsVisible(treeview_, kTaskManagerWebCoreImageCache)) { 781 wk_img_cache = 782 GetModelText(row, IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN); 783 } 784 std::string wk_scripts_cache; 785 if (TreeViewColumnIsVisible(treeview_, kTaskManagerWebCoreScriptsCache)) { 786 wk_scripts_cache = 787 GetModelText(row, IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN); 788 } 789 std::string wk_css_cache; 790 if (TreeViewColumnIsVisible(treeview_, kTaskManagerWebCoreCssCache)) { 791 wk_css_cache = 792 GetModelText(row, IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN); 793 } 794 std::string video_memory; 795 if (TreeViewColumnIsVisible(treeview_, kTaskManagerVideoMemory)) 796 video_memory = GetModelText(row, IDS_TASK_MANAGER_VIDEO_MEMORY_COLUMN); 797 std::string fps; 798 if (TreeViewColumnIsVisible(treeview_, kTaskManagerFPS)) 799 fps = GetModelText(row, IDS_TASK_MANAGER_FPS_COLUMN); 800 std::string sqlite_memory; 801 if (TreeViewColumnIsVisible(treeview_, kTaskManagerSqliteMemoryUsed)) { 802 sqlite_memory = 803 GetModelText(row, IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN); 804 } 805 806 std::string goats = 807 GetModelText(row, IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN); 808 809 bool is_background = model_->IsBackgroundResource(row) && 810 highlight_background_resources_; 811 gtk_list_store_set(process_list_, iter, 812 kTaskManagerIcon, icon, 813 kTaskManagerTask, task_markup, 814 kTaskManagerProfileName, profile_name.c_str(), 815 kTaskManagerSharedMem, shared_mem.c_str(), 816 kTaskManagerPrivateMem, priv_mem.c_str(), 817 kTaskManagerCPU, cpu.c_str(), 818 kTaskManagerNetwork, net.c_str(), 819 kTaskManagerProcessID, procid.c_str(), 820 kTaskManagerJavaScriptMemory, javascript_memory.c_str(), 821 kTaskManagerWebCoreImageCache, wk_img_cache.c_str(), 822 kTaskManagerWebCoreScriptsCache, wk_scripts_cache.c_str(), 823 kTaskManagerWebCoreCssCache, wk_css_cache.c_str(), 824 kTaskManagerVideoMemory, video_memory.c_str(), 825 kTaskManagerFPS, fps.c_str(), 826 kTaskManagerSqliteMemoryUsed, sqlite_memory.c_str(), 827 kTaskManagerGoatsTeleported, goats.c_str(), 828 kTaskManagerBackgroundColor, 829 is_background ? &kHighlightColor : NULL, 830 -1); 831 g_object_unref(icon); 832 g_free(task_markup); 833} 834 835void TaskManagerGtk::KillSelectedProcesses() { 836 GtkTreeSelection* selection = gtk_tree_view_get_selection( 837 GTK_TREE_VIEW(treeview_)); 838 839 GtkTreeModel* model; 840 GList* paths = gtk_tree_selection_get_selected_rows(selection, &model); 841 for (GList* item = paths; item; item = item->next) { 842 GtkTreePath* path = gtk_tree_model_sort_convert_path_to_child_path( 843 GTK_TREE_MODEL_SORT(process_list_sort_), 844 reinterpret_cast<GtkTreePath*>(item->data)); 845 int row = gtk_tree::GetRowNumForPath(path); 846 gtk_tree_path_free(path); 847 task_manager_->KillProcess(row); 848 } 849 g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL); 850 g_list_free(paths); 851} 852 853void TaskManagerGtk::ShowContextMenu(const gfx::Point& point, 854 guint32 event_time) { 855 if (!menu_controller_.get()) 856 menu_controller_.reset(new ContextMenuController(this)); 857 858 menu_controller_->RunMenu(point, event_time); 859} 860 861void TaskManagerGtk::OnLinkActivated() { 862 task_manager_->OpenAboutMemory(); 863} 864 865gint TaskManagerGtk::CompareImpl(GtkTreeModel* model, GtkTreeIter* a, 866 GtkTreeIter* b, int id) { 867 int row1 = gtk_tree::GetRowNumForIter(model, b); 868 int row2 = gtk_tree::GetRowNumForIter(model, a); 869 870 // When sorting by non-grouped attributes (e.g., Network), just do a normal 871 // sort. 872 if (!IsSharedByGroup(id)) 873 return model_->CompareValues(row1, row2, id); 874 875 // Otherwise, make sure grouped resources are shown together. 876 TaskManagerModel::GroupRange group_range1 = 877 model_->GetGroupRangeForResource(row1); 878 TaskManagerModel::GroupRange group_range2 = 879 model_->GetGroupRangeForResource(row2); 880 881 if (group_range1 == group_range2) { 882 // Sort within groups. 883 // We want the first-in-group row at the top, whether we are sorting up or 884 // down. 885 GtkSortType sort_type; 886 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(process_list_sort_), 887 NULL, &sort_type); 888 if (row1 == group_range1.first) 889 return sort_type == GTK_SORT_ASCENDING ? -1 : 1; 890 if (row2 == group_range2.first) 891 return sort_type == GTK_SORT_ASCENDING ? 1 : -1; 892 893 return model_->CompareValues(row1, row2, id); 894 } else { 895 // Sort between groups. 896 // Compare by the first-in-group rows so that the groups will stay together. 897 return model_->CompareValues(group_range1.first, group_range2.first, id); 898 } 899} 900 901void TaskManagerGtk::OnDestroy(GtkWidget* dialog) { 902 instance_ = NULL; 903 delete this; 904} 905 906void TaskManagerGtk::OnResponse(GtkWidget* dialog, int response_id) { 907 if (response_id == GTK_RESPONSE_DELETE_EVENT) { 908 // Store the dialog's size so we can restore it the next time it's opened. 909 if (g_browser_process->local_state()) { 910 gfx::Rect dialog_bounds = gtk_util::GetDialogBounds(GTK_WIDGET(dialog)); 911 912 DictionaryPrefUpdate update(g_browser_process->local_state(), 913 prefs::kTaskManagerWindowPlacement); 914 DictionaryValue* placement_pref = update.Get(); 915 // Note that we store left/top for consistency with Windows, but that we 916 // *don't* restore them. 917 placement_pref->SetInteger("left", dialog_bounds.x()); 918 placement_pref->SetInteger("top", dialog_bounds.y()); 919 placement_pref->SetInteger("right", dialog_bounds.right()); 920 placement_pref->SetInteger("bottom", dialog_bounds.bottom()); 921 placement_pref->SetBoolean("maximized", false); 922 } 923 924 instance_ = NULL; 925 delete this; 926 } else if (response_id == kTaskManagerResponseKill) { 927 KillSelectedProcesses(); 928 } else if (response_id == kTaskManagerAboutMemoryLink) { 929 OnLinkActivated(); 930 } else if (response_id == kTaskManagerPurgeMemory) { 931 MemoryPurger::PurgeAll(); 932 } 933} 934 935void TaskManagerGtk::OnTreeViewRealize(GtkTreeView* treeview) { 936 // Five columns show by default: the task column, the memory column, the CPU 937 // column, the network column, and the FPS column. Initially we set the task 938 // tolumn to take all the extra space, with the other columns being sized to 939 // fit the column names. Here we turn off the expand property of the first 940 // column (to make the table behave sanely when the user resizes it), and set 941 // the effective sizes of all five default columns to the automatically chosen 942 // sizes before any rows are added. This causes them to stay at those sizes 943 // even if the data would overflow, preventing a horizontal scroll bar from 944 // appearing due to the row data. 945 static const TaskManagerColumn dfl_columns[] = {kTaskManagerPrivateMem, 946 kTaskManagerCPU, 947 kTaskManagerNetwork, 948 kTaskManagerFPS}; 949 GtkTreeViewColumn* column = NULL; 950 gint width; 951 for (size_t i = 0; i < arraysize(dfl_columns); ++i) { 952 column = gtk_tree_view_get_column(treeview, 953 TreeViewColumnIndexFromID(dfl_columns[i])); 954 width = gtk_tree_view_column_get_width(column); 955 TreeViewColumnSetWidth(column, width); 956 } 957 // Do the task column separately since it's a little different. 958 column = gtk_tree_view_get_column(treeview, 959 TreeViewColumnIndexFromID(kTaskManagerTask)); 960 width = gtk_tree_view_column_get_width(column); 961 // Turn expanding back off to make resizing columns behave sanely. 962 gtk_tree_view_column_set_expand(column, FALSE); 963 TreeViewColumnSetWidth(column, width); 964} 965 966void TaskManagerGtk::OnSelectionChanged(GtkTreeSelection* selection) { 967 if (ignore_selection_changed_) 968 return; 969 AutoReset<bool> autoreset(&ignore_selection_changed_, true); 970 971 // The set of groups that should be selected. 972 std::set<TaskManagerModel::GroupRange> ranges; 973 bool selection_contains_browser_process = false; 974 975 GtkTreeModel* model; 976 GList* paths = gtk_tree_selection_get_selected_rows(selection, &model); 977 for (GList* item = paths; item; item = item->next) { 978 GtkTreePath* path = gtk_tree_model_sort_convert_path_to_child_path( 979 GTK_TREE_MODEL_SORT(process_list_sort_), 980 reinterpret_cast<GtkTreePath*>(item->data)); 981 int row = gtk_tree::GetRowNumForPath(path); 982 gtk_tree_path_free(path); 983 if (task_manager_->IsBrowserProcess(row)) 984 selection_contains_browser_process = true; 985 ranges.insert(model_->GetGroupRangeForResource(row)); 986 } 987 g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL); 988 g_list_free(paths); 989 990 for (std::set<TaskManagerModel::GroupRange>::iterator iter = ranges.begin(); 991 iter != ranges.end(); ++iter) { 992 for (int i = 0; i < iter->second; ++i) { 993 GtkTreePath* child_path = gtk_tree_path_new_from_indices(iter->first + i, 994 -1); 995 GtkTreePath* sort_path = gtk_tree_model_sort_convert_child_path_to_path( 996 GTK_TREE_MODEL_SORT(process_list_sort_), child_path); 997 gtk_tree_selection_select_path(selection, sort_path); 998 gtk_tree_path_free(child_path); 999 gtk_tree_path_free(sort_path); 1000 } 1001 } 1002 1003 bool sensitive = (paths != NULL) && !selection_contains_browser_process; 1004 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_), 1005 kTaskManagerResponseKill, sensitive); 1006} 1007 1008void TaskManagerGtk::OnRowActivated(GtkWidget* widget, 1009 GtkTreePath* path, 1010 GtkTreeViewColumn* column) { 1011 GtkTreePath* child_path = gtk_tree_model_sort_convert_path_to_child_path( 1012 GTK_TREE_MODEL_SORT(process_list_sort_), path); 1013 int row = gtk_tree::GetRowNumForPath(child_path); 1014 gtk_tree_path_free(child_path); 1015 task_manager_->ActivateProcess(row); 1016} 1017 1018gboolean TaskManagerGtk::OnButtonEvent(GtkWidget* widget, 1019 GdkEventButton* event) { 1020 // GTK does menu on mouse-up while views does menu on mouse-down, 1021 // so this function can be called from either signal. 1022 if (event->button == 3) { 1023 ShowContextMenu(gfx::Point(event->x_root, event->y_root), 1024 event->time); 1025 return TRUE; 1026 } 1027 1028 return FALSE; 1029} 1030 1031gboolean TaskManagerGtk::OnGtkAccelerator(GtkAccelGroup* accel_group, 1032 GObject* acceleratable, 1033 guint keyval, 1034 GdkModifierType modifier) { 1035 if (keyval == GDK_w && modifier == GDK_CONTROL_MASK) { 1036 // The GTK_RESPONSE_DELETE_EVENT response must be sent before the widget 1037 // is destroyed. The deleted object will receive gtk signals otherwise. 1038 gtk_dialog_response(GTK_DIALOG(dialog_), GTK_RESPONSE_DELETE_EVENT); 1039 } 1040 1041 return TRUE; 1042} 1043