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 |