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