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 |