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