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