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