Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(538)

Side by Side Diff: chrome/browser/ui/gtk/task_manager_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698