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