task_manager_view.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/task_manager/task_manager.h" 6 7#include "base/command_line.h" 8#include "base/compiler_specific.h" 9#include "base/metrics/stats_table.h" 10#include "base/utf_string_conversions.h" 11#include "chrome/app/chrome_command_ids.h" 12#include "chrome/browser/browser_list.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/browser_window.h" 15#include "chrome/browser/memory_purger.h" 16#include "chrome/browser/prefs/pref_service.h" 17#include "chrome/browser/ui/views/browser_dialogs.h" 18#include "chrome/common/chrome_switches.h" 19#include "chrome/common/pref_names.h" 20#include "grit/chromium_strings.h" 21#include "grit/generated_resources.h" 22#include "grit/theme_resources.h" 23#include "ui/base/l10n/l10n_util.h" 24#include "ui/base/models/table_model_observer.h" 25#include "views/accelerator.h" 26#include "views/background.h" 27#include "views/controls/button/native_button.h" 28#include "views/controls/link.h" 29#include "views/controls/menu/menu.h" 30#include "views/controls/table/group_table_view.h" 31#include "views/controls/table/table_view_observer.h" 32#include "views/layout/layout_constants.h" 33#include "views/widget/widget.h" 34#include "views/window/dialog_delegate.h" 35#include "views/window/window.h" 36 37// The task manager window default size. 38static const int kDefaultWidth = 460; 39static const int kDefaultHeight = 270; 40 41// Yellow highlight used when highlighting background resources. 42static const SkColor kBackgroundResourceHighlight = 43 SkColorSetRGB(0xff,0xf1,0xcd); 44 45namespace { 46 47//////////////////////////////////////////////////////////////////////////////// 48// TaskManagerTableModel class 49//////////////////////////////////////////////////////////////////////////////// 50 51class TaskManagerTableModel : public views::GroupTableModel, 52 public TaskManagerModelObserver { 53 public: 54 explicit TaskManagerTableModel(TaskManagerModel* model) 55 : model_(model), 56 observer_(NULL) { 57 model_->AddObserver(this); 58 } 59 60 ~TaskManagerTableModel() { 61 model_->RemoveObserver(this); 62 } 63 64 // GroupTableModel. 65 int RowCount() OVERRIDE; 66 string16 GetText(int row, int column) OVERRIDE; 67 SkBitmap GetIcon(int row) OVERRIDE; 68 void GetGroupRangeForItem(int item, views::GroupRange* range) OVERRIDE; 69 void SetObserver(ui::TableModelObserver* observer) OVERRIDE; 70 virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE; 71 72 // TaskManagerModelObserver. 73 virtual void OnModelChanged(); 74 virtual void OnItemsChanged(int start, int length); 75 virtual void OnItemsAdded(int start, int length); 76 virtual void OnItemsRemoved(int start, int length); 77 78 // Returns true if resource corresponding to |row| is a background resource. 79 bool IsBackgroundResource(int row); 80 81 private: 82 TaskManagerModel* model_; 83 ui::TableModelObserver* observer_; 84}; 85 86int TaskManagerTableModel::RowCount() { 87 return model_->ResourceCount(); 88} 89 90string16 TaskManagerTableModel::GetText(int row, int col_id) { 91 switch (col_id) { 92 case IDS_TASK_MANAGER_PAGE_COLUMN: // Process 93 return model_->GetResourceTitle(row); 94 95 case IDS_TASK_MANAGER_NET_COLUMN: // Net 96 return model_->GetResourceNetworkUsage(row); 97 98 case IDS_TASK_MANAGER_CPU_COLUMN: // CPU 99 if (!model_->IsResourceFirstInGroup(row)) 100 return string16(); 101 return model_->GetResourceCPUUsage(row); 102 103 case IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN: // Memory 104 if (!model_->IsResourceFirstInGroup(row)) 105 return string16(); 106 return model_->GetResourcePrivateMemory(row); 107 108 case IDS_TASK_MANAGER_SHARED_MEM_COLUMN: // Memory 109 if (!model_->IsResourceFirstInGroup(row)) 110 return string16(); 111 return model_->GetResourceSharedMemory(row); 112 113 case IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN: // Memory 114 if (!model_->IsResourceFirstInGroup(row)) 115 return string16(); 116 return model_->GetResourcePhysicalMemory(row); 117 118 case IDS_TASK_MANAGER_PROCESS_ID_COLUMN: 119 if (!model_->IsResourceFirstInGroup(row)) 120 return string16(); 121 return model_->GetResourceProcessId(row); 122 123 case IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN: // Goats Teleported! 124 return model_->GetResourceGoatsTeleported(row); 125 126 case IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN: 127 if (!model_->IsResourceFirstInGroup(row)) 128 return string16(); 129 return model_->GetResourceWebCoreImageCacheSize(row); 130 131 case IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN: 132 if (!model_->IsResourceFirstInGroup(row)) 133 return string16(); 134 return model_->GetResourceWebCoreScriptsCacheSize(row); 135 136 case IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN: 137 if (!model_->IsResourceFirstInGroup(row)) 138 return string16(); 139 return model_->GetResourceWebCoreCSSCacheSize(row); 140 141 case IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN: 142 if (!model_->IsResourceFirstInGroup(row)) 143 return string16(); 144 return model_->GetResourceSqliteMemoryUsed(row); 145 146 case IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN: 147 if (!model_->IsResourceFirstInGroup(row)) 148 return string16(); 149 return model_->GetResourceV8MemoryAllocatedSize(row); 150 151 default: 152 NOTREACHED(); 153 return string16(); 154 } 155} 156 157SkBitmap TaskManagerTableModel::GetIcon(int row) { 158 return model_->GetResourceIcon(row); 159} 160 161 162void TaskManagerTableModel::GetGroupRangeForItem(int item, 163 views::GroupRange* range) { 164 std::pair<int, int> range_pair = model_->GetGroupRangeForResource(item); 165 range->start = range_pair.first; 166 range->length = range_pair.second; 167} 168 169void TaskManagerTableModel::SetObserver(ui::TableModelObserver* observer) { 170 observer_ = observer; 171} 172 173int TaskManagerTableModel::CompareValues(int row1, int row2, int column_id) { 174 return model_->CompareValues(row1, row2, column_id); 175} 176 177void TaskManagerTableModel::OnModelChanged() { 178 if (observer_) 179 observer_->OnModelChanged(); 180} 181 182void TaskManagerTableModel::OnItemsChanged(int start, int length) { 183 if (observer_) 184 observer_->OnItemsChanged(start, length); 185} 186 187void TaskManagerTableModel::OnItemsAdded(int start, int length) { 188 if (observer_) 189 observer_->OnItemsAdded(start, length); 190 // There's a bug in the Windows ListView where inserting items with groups 191 // enabled puts them in the wrong position, so we will need to rebuild the 192 // list view in this case. 193 // (see: http://connect.microsoft.com/VisualStudio/feedback/details/115345/). 194 // 195 // Turns out, forcing a list view rebuild causes http://crbug.com/69391 196 // because items are added to the ListView one-at-a-time when initially 197 // displaying the TaskManager, resulting in many ListView rebuilds. So we are 198 // no longer forcing a rebuild for now because the current UI doesn't use 199 // groups - if we are going to add groups in the upcoming TaskManager UI 200 // revamp, we'll need to re-enable this call to OnModelChanged() and also add 201 // code to avoid doing multiple rebuilds on startup (maybe just generate a 202 // single OnModelChanged() call after the initial population). 203 204 // OnModelChanged(); 205} 206 207void TaskManagerTableModel::OnItemsRemoved(int start, int length) { 208 if (observer_) 209 observer_->OnItemsRemoved(start, length); 210 211 // We may need to change the indentation of some items if the topmost item 212 // in the group was removed, so update the view. 213 OnModelChanged(); 214} 215 216bool TaskManagerTableModel::IsBackgroundResource(int row) { 217 return model_->IsBackgroundResource(row); 218} 219 220// Thin wrapper around GroupTableView to enable setting the background 221// resource highlight color. 222class BackgroundColorGroupTableView : public views::GroupTableView { 223 public: 224 BackgroundColorGroupTableView(TaskManagerTableModel* model, 225 std::vector<ui::TableColumn> columns, 226 bool highlight_background_resources) 227 : views::GroupTableView(model, columns, views::ICON_AND_TEXT, 228 false, true, true, true), 229 model_(model) { 230 SetCustomColorsEnabled(highlight_background_resources); 231 } 232 233 virtual ~BackgroundColorGroupTableView() {} 234 235 private: 236 virtual bool GetCellColors(int model_row, 237 int column, 238 ItemColor* foreground, 239 ItemColor* background, 240 LOGFONT* logfont) { 241 if (!model_->IsBackgroundResource(model_row)) 242 return false; 243 244 // Render background resources with a yellow highlight. 245 background->color_is_set = true; 246 background->color = kBackgroundResourceHighlight; 247 foreground->color_is_set = false; 248 return true; 249 } 250 251 TaskManagerTableModel* model_; 252}; 253 254// The Task manager UI container. 255class TaskManagerView : public views::View, 256 public views::ButtonListener, 257 public views::DialogDelegate, 258 public views::TableViewObserver, 259 public views::LinkController, 260 public views::ContextMenuController, 261 public views::Menu::Delegate { 262 public: 263 explicit TaskManagerView(bool highlight_background_resources); 264 virtual ~TaskManagerView(); 265 266 // Shows the Task manager window, or re-activates an existing one. If 267 // |highlight_background_resources| is true, highlights the background 268 // resources in the resource display. 269 static void Show(bool highlight_background_resources); 270 271 // views::View 272 virtual void Layout(); 273 virtual gfx::Size GetPreferredSize(); 274 virtual void ViewHierarchyChanged(bool is_add, views::View* parent, 275 views::View* child); 276 277 // ButtonListener implementation. 278 virtual void ButtonPressed(views::Button* sender, const views::Event& event); 279 280 // views::DialogDelegate 281 virtual bool CanResize() const; 282 virtual bool CanMaximize() const; 283 virtual bool ExecuteWindowsCommand(int command_id); 284 virtual std::wstring GetWindowTitle() const; 285 virtual std::wstring GetWindowName() const; 286 virtual int GetDialogButtons() const; 287 virtual void WindowClosing(); 288 virtual views::View* GetContentsView(); 289 290 // views::TableViewObserver implementation. 291 virtual void OnSelectionChanged(); 292 virtual void OnDoubleClick(); 293 virtual void OnKeyDown(ui::KeyboardCode keycode); 294 295 // views::LinkController implementation. 296 virtual void LinkActivated(views::Link* source, int event_flags); 297 298 // Called by the column picker to pick up any new stat counters that 299 // may have appeared since last time. 300 void UpdateStatsCounters(); 301 302 // Menu::Delegate 303 virtual void ShowContextMenu(views::View* source, 304 const gfx::Point& p, 305 bool is_mouse_gesture); 306 virtual bool IsItemChecked(int id) const; 307 virtual void ExecuteCommand(int id); 308 309 private: 310 // Creates the child controls. 311 void Init(); 312 313 // Initializes the state of the always-on-top setting as the window is shown. 314 void InitAlwaysOnTopState(); 315 316 // Activates the tab associated with the focused row. 317 void ActivateFocusedTab(); 318 319 // Adds an always on top item to the window's system menu. 320 void AddAlwaysOnTopSystemMenuItem(); 321 322 // Restores saved always on top state from a previous session. 323 bool GetSavedAlwaysOnTopState(bool* always_on_top) const; 324 325 views::NativeButton* purge_memory_button_; 326 views::NativeButton* kill_button_; 327 views::Link* about_memory_link_; 328 views::GroupTableView* tab_table_; 329 330 TaskManager* task_manager_; 331 332 TaskManagerModel* model_; 333 334 // all possible columns, not necessarily visible 335 std::vector<ui::TableColumn> columns_; 336 337 scoped_ptr<TaskManagerTableModel> table_model_; 338 339 // True when the Task Manager window should be shown on top of other windows. 340 bool is_always_on_top_; 341 342 // True when the Task Manager should highlight background resources. 343 bool highlight_background_resources_; 344 345 // We need to own the text of the menu, the Windows API does not copy it. 346 std::wstring always_on_top_menu_text_; 347 348 // An open Task manager window. There can only be one open at a time. This 349 // is reset to NULL when the window is closed. 350 static TaskManagerView* instance_; 351 352 DISALLOW_COPY_AND_ASSIGN(TaskManagerView); 353}; 354 355// static 356TaskManagerView* TaskManagerView::instance_ = NULL; 357 358 359TaskManagerView::TaskManagerView(bool highlight_background_resources) 360 : purge_memory_button_(NULL), 361 task_manager_(TaskManager::GetInstance()), 362 model_(TaskManager::GetInstance()->model()), 363 is_always_on_top_(false), 364 highlight_background_resources_(highlight_background_resources) { 365 Init(); 366} 367 368TaskManagerView::~TaskManagerView() { 369 // Delete child views now, while our table model still exists. 370 RemoveAllChildViews(true); 371} 372 373void TaskManagerView::Init() { 374 table_model_.reset(new TaskManagerTableModel(model_)); 375 376 // Page column has no header label. 377 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PAGE_COLUMN, 378 ui::TableColumn::LEFT, -1, 1)); 379 columns_.back().sortable = true; 380 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PHYSICAL_MEM_COLUMN, 381 ui::TableColumn::RIGHT, -1, 0)); 382 columns_.back().sortable = true; 383 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_SHARED_MEM_COLUMN, 384 ui::TableColumn::RIGHT, -1, 0)); 385 columns_.back().sortable = true; 386 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN, 387 ui::TableColumn::RIGHT, -1, 0)); 388 columns_.back().sortable = true; 389 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_CPU_COLUMN, 390 ui::TableColumn::RIGHT, -1, 0)); 391 columns_.back().sortable = true; 392 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_NET_COLUMN, 393 ui::TableColumn::RIGHT, -1, 0)); 394 columns_.back().sortable = true; 395 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_PROCESS_ID_COLUMN, 396 ui::TableColumn::RIGHT, -1, 0)); 397 columns_.back().sortable = true; 398 columns_.push_back(ui::TableColumn( 399 IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN, 400 ui::TableColumn::RIGHT, -1, 0)); 401 columns_.back().sortable = true; 402 columns_.push_back(ui::TableColumn( 403 IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN, 404 ui::TableColumn::RIGHT, -1, 0)); 405 columns_.back().sortable = true; 406 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, 407 ui::TableColumn::RIGHT, -1, 0)); 408 columns_.back().sortable = true; 409 columns_.push_back(ui::TableColumn(IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN, 410 ui::TableColumn::RIGHT, -1, 0)); 411 columns_.back().sortable = true; 412 columns_.push_back( 413 ui::TableColumn(IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN, 414 ui::TableColumn::RIGHT, -1, 0)); 415 columns_.back().sortable = true; 416 417 tab_table_ = new BackgroundColorGroupTableView( 418 table_model_.get(), columns_, highlight_background_resources_); 419 420 // Hide some columns by default 421 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_PROCESS_ID_COLUMN, false); 422 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_SHARED_MEM_COLUMN, false); 423 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_PRIVATE_MEM_COLUMN, false); 424 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_IMAGE_CACHE_COLUMN, 425 false); 426 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_SCRIPTS_CACHE_COLUMN, 427 false); 428 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_WEBCORE_CSS_CACHE_COLUMN, 429 false); 430 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_SQLITE_MEMORY_USED_COLUMN, 431 false); 432 tab_table_->SetColumnVisibility( 433 IDS_TASK_MANAGER_JAVASCRIPT_MEMORY_ALLOCATED_COLUMN, false); 434 tab_table_->SetColumnVisibility(IDS_TASK_MANAGER_GOATS_TELEPORTED_COLUMN, 435 false); 436 437 UpdateStatsCounters(); 438 tab_table_->SetObserver(this); 439 tab_table_->SetContextMenuController(this); 440 SetContextMenuController(this); 441 // If we're running with --purge-memory-button, add a "Purge memory" button. 442 if (CommandLine::ForCurrentProcess()->HasSwitch( 443 switches::kPurgeMemoryButton)) { 444 purge_memory_button_ = new views::NativeButton(this, 445 UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_PURGE_MEMORY))); 446 } 447 kill_button_ = new views::NativeButton( 448 this, UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_KILL))); 449 kill_button_->AddAccelerator(views::Accelerator(ui::VKEY_E, 450 false, false, false)); 451 kill_button_->SetAccessibleKeyboardShortcut(L"E"); 452 about_memory_link_ = new views::Link(UTF16ToWide( 453 l10n_util::GetStringUTF16(IDS_TASK_MANAGER_ABOUT_MEMORY_LINK))); 454 about_memory_link_->SetController(this); 455 456 // Makes sure our state is consistent. 457 OnSelectionChanged(); 458} 459 460void TaskManagerView::UpdateStatsCounters() { 461 base::StatsTable* stats = base::StatsTable::current(); 462 if (stats != NULL) { 463 int max = stats->GetMaxCounters(); 464 // skip the first row (it's header data) 465 for (int i = 1; i < max; i++) { 466 const char* row = stats->GetRowName(i); 467 if (row != NULL && row[0] != '\0' && !tab_table_->HasColumn(i)) { 468 // TODO(erikkay): Use l10n to get display names for stats. Right 469 // now we're just displaying the internal counter name. Perhaps 470 // stat names not in the string table would be filtered out. 471 // TODO(erikkay): Width is hard-coded right now, so many column 472 // names are clipped. 473 ui::TableColumn col(i, ASCIIToUTF16(row), ui::TableColumn::RIGHT, 90, 474 0); 475 col.sortable = true; 476 columns_.push_back(col); 477 tab_table_->AddColumn(col); 478 } 479 } 480 } 481} 482 483void TaskManagerView::ViewHierarchyChanged(bool is_add, 484 views::View* parent, 485 views::View* child) { 486 // Since we want the Kill button and the Memory Details link to show up in 487 // the same visual row as the close button, which is provided by the 488 // framework, we must add the buttons to the non-client view, which is the 489 // parent of this view. Similarly, when we're removed from the view 490 // hierarchy, we must take care to clean up those items as well. 491 if (child == this) { 492 if (is_add) { 493 parent->AddChildView(about_memory_link_); 494 if (purge_memory_button_) 495 parent->AddChildView(purge_memory_button_); 496 parent->AddChildView(kill_button_); 497 AddChildView(tab_table_); 498 } else { 499 parent->RemoveChildView(kill_button_); 500 if (purge_memory_button_) 501 parent->RemoveChildView(purge_memory_button_); 502 parent->RemoveChildView(about_memory_link_); 503 } 504 } 505} 506 507void TaskManagerView::Layout() { 508 // kPanelHorizMargin is too big. 509 const int kTableButtonSpacing = 12; 510 511 gfx::Size size = kill_button_->GetPreferredSize(); 512 int prefered_width = size.width(); 513 int prefered_height = size.height(); 514 515 tab_table_->SetBounds(x() + kPanelHorizMargin, 516 y() + kPanelVertMargin, 517 width() - 2 * kPanelHorizMargin, 518 height() - 2 * kPanelVertMargin - prefered_height); 519 520 // y-coordinate of button top left. 521 gfx::Rect parent_bounds = parent()->GetContentsBounds(); 522 int y_buttons = 523 parent_bounds.bottom() - prefered_height - views::kButtonVEdgeMargin; 524 525 kill_button_->SetBounds(x() + width() - prefered_width - kPanelHorizMargin, 526 y_buttons, 527 prefered_width, 528 prefered_height); 529 530 if (purge_memory_button_) { 531 size = purge_memory_button_->GetPreferredSize(); 532 purge_memory_button_->SetBounds( 533 kill_button_->x() - size.width() - 534 views::kUnrelatedControlHorizontalSpacing, 535 y_buttons, size.width(), size.height()); 536 } 537 538 size = about_memory_link_->GetPreferredSize(); 539 int link_prefered_width = size.width(); 540 int link_prefered_height = size.height(); 541 // center between the two buttons horizontally, and line up with 542 // bottom of buttons vertically. 543 int link_y_offset = std::max(0, prefered_height - link_prefered_height) / 2; 544 about_memory_link_->SetBounds( 545 x() + kPanelHorizMargin, 546 y_buttons + prefered_height - link_prefered_height - link_y_offset, 547 link_prefered_width, 548 link_prefered_height); 549} 550 551gfx::Size TaskManagerView::GetPreferredSize() { 552 return gfx::Size(kDefaultWidth, kDefaultHeight); 553} 554 555// static 556void TaskManagerView::Show(bool highlight_background_resources) { 557 if (instance_) { 558 if (instance_->highlight_background_resources_ != 559 highlight_background_resources) { 560 instance_->window()->Close(); 561 } else { 562 // If there's a Task manager window open already, just activate it. 563 instance_->window()->Activate(); 564 return; 565 } 566 } 567 instance_ = new TaskManagerView(highlight_background_resources); 568 views::Window::CreateChromeWindow(NULL, gfx::Rect(), instance_); 569 instance_->InitAlwaysOnTopState(); 570 instance_->model_->StartUpdating(); 571 instance_->window()->Show(); 572 573 // Set the initial focus to the list of tasks. 574 views::FocusManager* focus_manager = instance_->GetFocusManager(); 575 if (focus_manager) 576 focus_manager->SetFocusedView(instance_->tab_table_); 577} 578 579// ButtonListener implementation. 580void TaskManagerView::ButtonPressed( 581 views::Button* sender, const views::Event& event) { 582 if (purge_memory_button_ && (sender == purge_memory_button_)) { 583 MemoryPurger::PurgeAll(); 584 } else { 585 DCHECK_EQ(sender, kill_button_); 586 for (views::TableSelectionIterator iter = tab_table_->SelectionBegin(); 587 iter != tab_table_->SelectionEnd(); ++iter) 588 task_manager_->KillProcess(*iter); 589 } 590} 591 592// DialogDelegate implementation. 593bool TaskManagerView::CanResize() const { 594 return true; 595} 596 597bool TaskManagerView::CanMaximize() const { 598 return true; 599} 600 601bool TaskManagerView::ExecuteWindowsCommand(int command_id) { 602 if (command_id == IDC_ALWAYS_ON_TOP) { 603 is_always_on_top_ = !is_always_on_top_; 604 605 // Change the menu check state. 606 HMENU system_menu = GetSystemMenu(GetWindow()->GetNativeWindow(), FALSE); 607 MENUITEMINFO menu_info; 608 memset(&menu_info, 0, sizeof(MENUITEMINFO)); 609 menu_info.cbSize = sizeof(MENUITEMINFO); 610 BOOL r = GetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, 611 FALSE, &menu_info); 612 DCHECK(r); 613 menu_info.fMask = MIIM_STATE; 614 if (is_always_on_top_) 615 menu_info.fState = MFS_CHECKED; 616 r = SetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, FALSE, &menu_info); 617 618 // Now change the actual window's behavior. 619 window()->SetIsAlwaysOnTop(is_always_on_top_); 620 621 // Save the state. 622 if (g_browser_process->local_state()) { 623 DictionaryValue* window_preferences = 624 g_browser_process->local_state()->GetMutableDictionary( 625 WideToUTF8(GetWindowName()).c_str()); 626 window_preferences->SetBoolean("always_on_top", is_always_on_top_); 627 } 628 return true; 629 } 630 return false; 631} 632 633std::wstring TaskManagerView::GetWindowTitle() const { 634 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER_TITLE)); 635} 636 637std::wstring TaskManagerView::GetWindowName() const { 638 return UTF8ToWide(prefs::kTaskManagerWindowPlacement); 639} 640 641int TaskManagerView::GetDialogButtons() const { 642 return MessageBoxFlags::DIALOGBUTTON_NONE; 643} 644 645void TaskManagerView::WindowClosing() { 646 // Now that the window is closed, we can allow a new one to be opened. 647 instance_ = NULL; 648 task_manager_->OnWindowClosed(); 649} 650 651views::View* TaskManagerView::GetContentsView() { 652 return this; 653} 654 655// views::TableViewObserver implementation. 656void TaskManagerView::OnSelectionChanged() { 657 bool selection_contains_browser_process = false; 658 for (views::TableSelectionIterator iter = tab_table_->SelectionBegin(); 659 iter != tab_table_->SelectionEnd(); ++iter) { 660 if (task_manager_->IsBrowserProcess(*iter)) { 661 selection_contains_browser_process = true; 662 break; 663 } 664 } 665 kill_button_->SetEnabled(!selection_contains_browser_process && 666 tab_table_->SelectedRowCount() > 0); 667} 668 669void TaskManagerView::OnDoubleClick() { 670 ActivateFocusedTab(); 671} 672 673void TaskManagerView::OnKeyDown(ui::KeyboardCode keycode) { 674 if (keycode == ui::VKEY_RETURN) 675 ActivateFocusedTab(); 676} 677 678// views::LinkController implementation 679void TaskManagerView::LinkActivated(views::Link* source, int event_flags) { 680 DCHECK(source == about_memory_link_); 681 task_manager_->OpenAboutMemory(); 682} 683 684void TaskManagerView::ShowContextMenu(views::View* source, 685 const gfx::Point& p, 686 bool is_mouse_gesture) { 687 UpdateStatsCounters(); 688 scoped_ptr<views::Menu> menu(views::Menu::Create( 689 this, views::Menu::TOPLEFT, source->GetWidget()->GetNativeView())); 690 for (std::vector<ui::TableColumn>::iterator i = 691 columns_.begin(); i != columns_.end(); ++i) { 692 menu->AppendMenuItem(i->id, l10n_util::GetStringUTF16(i->id), 693 views::Menu::CHECKBOX); 694 } 695 menu->RunMenuAt(p.x(), p.y()); 696} 697 698bool TaskManagerView::IsItemChecked(int id) const { 699 return tab_table_->IsColumnVisible(id); 700} 701 702void TaskManagerView::ExecuteCommand(int id) { 703 tab_table_->SetColumnVisibility(id, !tab_table_->IsColumnVisible(id)); 704} 705 706void TaskManagerView::InitAlwaysOnTopState() { 707 is_always_on_top_ = false; 708 if (GetSavedAlwaysOnTopState(&is_always_on_top_)) 709 window()->SetIsAlwaysOnTop(is_always_on_top_); 710 AddAlwaysOnTopSystemMenuItem(); 711} 712 713void TaskManagerView::ActivateFocusedTab() { 714 int row_count = tab_table_->RowCount(); 715 for (int i = 0; i < row_count; ++i) { 716 if (tab_table_->ItemHasTheFocus(i)) { 717 task_manager_->ActivateProcess(i); 718 break; 719 } 720 } 721} 722 723void TaskManagerView::AddAlwaysOnTopSystemMenuItem() { 724 // The Win32 API requires that we own the text. 725 always_on_top_menu_text_ = 726 UTF16ToWide(l10n_util::GetStringUTF16(IDS_ALWAYS_ON_TOP)); 727 728 // Let's insert a menu to the window. 729 HMENU system_menu = ::GetSystemMenu(GetWindow()->GetNativeWindow(), FALSE); 730 int index = ::GetMenuItemCount(system_menu) - 1; 731 if (index < 0) { 732 // Paranoia check. 733 NOTREACHED(); 734 index = 0; 735 } 736 // First we add the separator. 737 MENUITEMINFO menu_info; 738 memset(&menu_info, 0, sizeof(MENUITEMINFO)); 739 menu_info.cbSize = sizeof(MENUITEMINFO); 740 menu_info.fMask = MIIM_FTYPE; 741 menu_info.fType = MFT_SEPARATOR; 742 ::InsertMenuItem(system_menu, index, TRUE, &menu_info); 743 744 // Then the actual menu. 745 menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE; 746 menu_info.fType = MFT_STRING; 747 menu_info.fState = MFS_ENABLED; 748 if (is_always_on_top_) 749 menu_info.fState |= MFS_CHECKED; 750 menu_info.wID = IDC_ALWAYS_ON_TOP; 751 menu_info.dwTypeData = const_cast<wchar_t*>(always_on_top_menu_text_.c_str()); 752 ::InsertMenuItem(system_menu, index, TRUE, &menu_info); 753} 754 755bool TaskManagerView::GetSavedAlwaysOnTopState(bool* always_on_top) const { 756 if (!g_browser_process->local_state()) 757 return false; 758 759 const DictionaryValue* dictionary = 760 g_browser_process->local_state()->GetDictionary( 761 WideToUTF8(GetWindowName()).c_str()); 762 return dictionary && 763 dictionary->GetBoolean("always_on_top", always_on_top) && always_on_top; 764} 765 766} // namespace 767 768namespace browser { 769 770// Declared in browser_dialogs.h so others don't need to depend on our header. 771void ShowTaskManager() { 772 TaskManagerView::Show(false); 773} 774 775void ShowBackgroundPages() { 776 TaskManagerView::Show(true); 777} 778 779} // namespace browser 780