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