Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ash/wm/overview/window_selector.h" | 5 #include "ash/wm/overview/window_selector.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 85 }; | 85 }; |
| 86 | 86 |
| 87 // A comparator for locating a selectable window given a targeted window. | 87 // A comparator for locating a selectable window given a targeted window. |
| 88 struct WindowSelectorItemTargetComparator | 88 struct WindowSelectorItemTargetComparator |
| 89 : public std::unary_function<WindowSelectorItem*, bool> { | 89 : public std::unary_function<WindowSelectorItem*, bool> { |
| 90 explicit WindowSelectorItemTargetComparator(const aura::Window* target_window) | 90 explicit WindowSelectorItemTargetComparator(const aura::Window* target_window) |
| 91 : target(target_window) { | 91 : target(target_window) { |
| 92 } | 92 } |
| 93 | 93 |
| 94 bool operator()(WindowSelectorItem* window) const { | 94 bool operator()(WindowSelectorItem* window) const { |
| 95 return window->Contains(target); | 95 return window->GetWindow() == target; |
| 96 } | 96 } |
| 97 | 97 |
| 98 const aura::Window* target; | 98 const aura::Window* target; |
| 99 }; | 99 }; |
| 100 | 100 |
| 101 // A comparator for locating a selector item for a given root. | 101 // A comparator for locating a selector item for a given root. |
| 102 struct WindowSelectorItemForRoot | 102 struct WindowSelectorItemForRoot |
| 103 : public std::unary_function<WindowSelectorItem*, bool> { | 103 : public std::unary_function<WindowSelectorItem*, bool> { |
| 104 explicit WindowSelectorItemForRoot(const aura::Window* root) | 104 explicit WindowSelectorItemForRoot(const aura::Window* root) |
| 105 : root_window(root) { | 105 : root_window(root) { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 textfield->RequestFocus(); | 210 textfield->RequestFocus(); |
| 211 | 211 |
| 212 return widget; | 212 return widget; |
| 213 } | 213 } |
| 214 | 214 |
| 215 } // namespace | 215 } // namespace |
| 216 | 216 |
| 217 const int WindowSelector::kTextFilterBottomEdge = | 217 const int WindowSelector::kTextFilterBottomEdge = |
| 218 kTextFilterDistanceFromTop + kTextFilterHeight; | 218 kTextFilterDistanceFromTop + kTextFilterHeight; |
| 219 | 219 |
| 220 WindowSelector::WindowSelector(const WindowList& windows, | 220 WindowSelector::WindowSelector(WindowSelectorDelegate* delegate) |
| 221 WindowSelectorDelegate* delegate) | |
| 222 : delegate_(delegate), | 221 : delegate_(delegate), |
| 223 restore_focus_window_(aura::client::GetFocusClient( | 222 restore_focus_window_(aura::client::GetFocusClient( |
| 224 Shell::GetPrimaryRootWindow())->GetFocusedWindow()), | 223 Shell::GetPrimaryRootWindow())->GetFocusedWindow()), |
| 225 ignore_activations_(false), | 224 ignore_activations_(false), |
| 226 selected_grid_index_(0), | 225 selected_grid_index_(0), |
| 227 overview_start_time_(base::Time::Now()), | 226 overview_start_time_(base::Time::Now()), |
| 228 num_key_presses_(0), | 227 num_key_presses_(0), |
| 229 num_items_(0), | 228 num_items_(0), |
| 230 showing_selection_widget_(false), | 229 showing_selection_widget_(false), |
| 231 text_filter_string_length_(0), | 230 text_filter_string_length_(0), |
| 232 num_times_textfield_cleared_(0) { | 231 num_times_textfield_cleared_(0), |
| 232 restoring_minimized_windows_(false) { | |
| 233 DCHECK(delegate_); | 233 DCHECK(delegate_); |
| 234 Shell* shell = Shell::GetInstance(); | 234 } |
| 235 shell->OnOverviewModeStarting(); | |
| 236 | 235 |
| 236 WindowSelector::~WindowSelector() { | |
| 237 RemoveAllObservers(); | |
| 238 } | |
| 239 | |
| 240 // NOTE: The work done in Init() is not done in the constructor because it may | |
| 241 // cause other, unrelated classes, (ie PanelLayoutManager) to make indirect | |
| 242 // calls to restoring_minimized_windows() on a partially constructed object. | |
| 243 void WindowSelector::Init(const WindowList& windows) { | |
| 237 if (restore_focus_window_) | 244 if (restore_focus_window_) |
| 238 restore_focus_window_->AddObserver(this); | 245 restore_focus_window_->AddObserver(this); |
| 239 | 246 |
| 240 const aura::Window::Windows root_windows = Shell::GetAllRootWindows(); | 247 const aura::Window::Windows root_windows = Shell::GetAllRootWindows(); |
| 241 for (aura::Window::Windows::const_iterator iter = root_windows.begin(); | 248 for (aura::Window::Windows::const_iterator iter = root_windows.begin(); |
| 242 iter != root_windows.end(); iter++) { | 249 iter != root_windows.end(); iter++) { |
| 243 // Observed switchable containers for newly created windows on all root | 250 // Observed switchable containers for newly created windows on all root |
| 244 // windows. | 251 // windows. |
| 245 for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) { | 252 for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) { |
| 246 aura::Window* container = Shell::GetContainer(*iter, | 253 aura::Window* container = Shell::GetContainer(*iter, |
| 247 kSwitchableWindowContainerIds[i]); | 254 kSwitchableWindowContainerIds[i]); |
| 248 container->AddObserver(this); | 255 container->AddObserver(this); |
| 249 observed_windows_.insert(container); | 256 observed_windows_.insert(container); |
| 250 } | 257 } |
| 251 | 258 |
| 252 // Hide the callout widgets for panels. It is safe to call this for | 259 // Hide the callout widgets for panels. It is safe to call this for |
| 253 // root windows that don't contain any panel windows. | 260 // root windows that don't contain any panel windows. |
| 254 static_cast<PanelLayoutManager*>( | 261 static_cast<PanelLayoutManager*>( |
| 255 Shell::GetContainer(*iter, kShellWindowId_PanelContainer) | 262 Shell::GetContainer(*iter, kShellWindowId_PanelContainer) |
| 256 ->layout_manager())->SetShowCalloutWidgets(false); | 263 ->layout_manager())->SetShowCalloutWidgets(false); |
| 257 | 264 |
| 258 scoped_ptr<WindowGrid> grid(new WindowGrid(*iter, windows, this)); | 265 scoped_ptr<WindowGrid> grid(new WindowGrid(*iter, windows, this)); |
| 259 if (grid->empty()) | 266 if (grid->empty()) |
| 260 continue; | 267 continue; |
| 261 num_items_ += grid->size(); | 268 num_items_ += grid->size(); |
| 262 grid_list_.push_back(grid.release()); | 269 grid_list_.push_back(grid.release()); |
| 263 } | 270 } |
| 264 | 271 |
| 265 // Do not call PrepareForOverview until all items are added to window_list_ as | 272 { |
| 266 // we don't want to cause any window updates until all windows in overview | 273 // The calls to WindowGrid::PrepareForOverview() and CreateTextFilter(...) |
| 267 // are observed. See http://crbug.com/384495. | 274 // requires some LayoutManagers (ie PanelLayoutManager) to perform layouts |
| 268 for (ScopedVector<WindowGrid>::iterator iter = grid_list_.begin(); | 275 // so that windows are correctly visible and properly animated in overview |
| 269 iter != grid_list_.end(); ++iter) { | 276 // mode. Otherwise these layouts should be suppressed during overview mode |
| 270 (*iter)->PrepareForOverview(); | 277 // so they don't conflict with overview mode animations. The |
| 271 (*iter)->PositionWindows(true); | 278 // |restoring_minimized_windows_| flag enables the PanelLayoutManager to |
| 279 // make this decision. | |
| 280 base::AutoReset<bool> auto_restoring_minimized_windows( | |
| 281 &restoring_minimized_windows_, true); | |
| 282 | |
| 283 // Do not call PrepareForOverview until all items are added to window_list_ | |
| 284 // as we don't want to cause any window updates until all windows in | |
| 285 // overview are observed. See http://crbug.com/384495. | |
| 286 for (auto window_grid : grid_list_) { | |
| 287 window_grid->PrepareForOverview(); | |
| 288 window_grid->PositionWindows(true); | |
| 289 } | |
| 290 | |
| 291 text_filter_widget_.reset( | |
| 292 CreateTextFilter(this, Shell::GetPrimaryRootWindow())); | |
| 272 } | 293 } |
| 273 | 294 |
| 274 DCHECK(!grid_list_.empty()); | 295 DCHECK(!grid_list_.empty()); |
| 275 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", num_items_); | 296 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", num_items_); |
| 276 | 297 |
| 277 text_filter_widget_.reset( | 298 Shell* shell = Shell::GetInstance(); |
| 278 CreateTextFilter(this, Shell::GetPrimaryRootWindow())); | |
| 279 | 299 |
| 280 shell->activation_client()->AddObserver(this); | 300 shell->activation_client()->AddObserver(this); |
| 281 | 301 |
| 282 shell->GetScreen()->AddObserver(this); | 302 shell->GetScreen()->AddObserver(this); |
| 283 shell->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW); | 303 shell->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW); |
| 284 HideAndTrackNonOverviewWindows(); | 304 HideAndTrackNonOverviewWindows(); |
| 285 // Send an a11y alert. | 305 // Send an a11y alert. |
| 286 shell->accessibility_delegate()->TriggerAccessibilityAlert( | 306 shell->accessibility_delegate()->TriggerAccessibilityAlert( |
| 287 ui::A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED); | 307 ui::A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED); |
| 288 | 308 |
| 289 UpdateShelfVisibility(); | 309 UpdateShelfVisibility(); |
| 290 } | 310 } |
| 291 | 311 |
| 292 WindowSelector::~WindowSelector() { | 312 // NOTE: The work done in Shutdown() is not done in the destructor because it |
| 293 Shell* shell = Shell::GetInstance(); | 313 // may cause other, unrelated classes, (ie PanelLayoutManager) to make indirect |
| 294 | 314 // calls to restoring_minimized_windows() on a partially destructed object. |
| 315 void WindowSelector::Shutdown() { | |
| 295 ResetFocusRestoreWindow(true); | 316 ResetFocusRestoreWindow(true); |
| 296 for (std::set<aura::Window*>::iterator iter = observed_windows_.begin(); | 317 RemoveAllObservers(); |
| 297 iter != observed_windows_.end(); ++iter) { | |
| 298 (*iter)->RemoveObserver(this); | |
| 299 } | |
| 300 shell->activation_client()->RemoveObserver(this); | |
| 301 | 318 |
| 302 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); | 319 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); |
| 303 for (aura::Window::Windows::const_iterator iter = root_windows.begin(); | 320 for (aura::Window::Windows::const_iterator iter = root_windows.begin(); |
| 304 iter != root_windows.end(); iter++) { | 321 iter != root_windows.end(); iter++) { |
| 305 // Un-hide the callout widgets for panels. It is safe to call this for | 322 // Un-hide the callout widgets for panels. It is safe to call this for |
| 306 // root_windows that don't contain any panel windows. | 323 // root_windows that don't contain any panel windows. |
| 307 static_cast<PanelLayoutManager*>( | 324 static_cast<PanelLayoutManager*>( |
| 308 Shell::GetContainer(*iter, kShellWindowId_PanelContainer) | 325 Shell::GetContainer(*iter, kShellWindowId_PanelContainer) |
| 309 ->layout_manager())->SetShowCalloutWidgets(true); | 326 ->layout_manager())->SetShowCalloutWidgets(true); |
| 310 } | 327 } |
| 311 | 328 |
| 312 const aura::WindowTracker::Windows hidden_windows(hidden_windows_.windows()); | 329 const aura::WindowTracker::Windows hidden_windows(hidden_windows_.windows()); |
| 313 for (aura::WindowTracker::Windows::const_iterator iter = | 330 for (aura::WindowTracker::Windows::const_iterator iter = |
| 314 hidden_windows.begin(); iter != hidden_windows.end(); ++iter) { | 331 hidden_windows.begin(); iter != hidden_windows.end(); ++iter) { |
| 315 ScopedOverviewAnimationSettings animation_settings( | 332 ScopedOverviewAnimationSettings animation_settings( |
| 316 OverviewAnimationType::OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS, | 333 OverviewAnimationType::OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS, |
| 317 *iter); | 334 *iter); |
| 318 (*iter)->layer()->SetOpacity(1); | 335 (*iter)->layer()->SetOpacity(1); |
| 319 (*iter)->Show(); | 336 (*iter)->Show(); |
| 320 } | 337 } |
| 321 | 338 |
| 322 shell->GetScreen()->RemoveObserver(this); | |
| 323 | |
| 324 size_t remaining_items = 0; | 339 size_t remaining_items = 0; |
| 325 for (ScopedVector<WindowGrid>::iterator iter = grid_list_.begin(); | 340 for (auto window_grid : grid_list_) { |
| 326 iter != grid_list_.end(); iter++) { | 341 for (auto window_selector_item : window_grid->window_list()) |
| 327 remaining_items += (*iter)->size(); | 342 window_selector_item->RestoreWindow(); |
| 343 remaining_items += window_grid->size(); | |
| 328 } | 344 } |
| 329 | 345 |
| 330 DCHECK(num_items_ >= remaining_items); | 346 DCHECK(num_items_ >= remaining_items); |
| 331 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.OverviewClosedItems", | 347 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.OverviewClosedItems", |
| 332 num_items_ - remaining_items); | 348 num_items_ - remaining_items); |
| 333 UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.TimeInOverview", | 349 UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.TimeInOverview", |
| 334 base::Time::Now() - overview_start_time_); | 350 base::Time::Now() - overview_start_time_); |
| 335 | 351 |
| 336 // Record metrics related to text filtering. | 352 // Record metrics related to text filtering. |
| 337 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringStringLength", | 353 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringStringLength", |
| 338 text_filter_string_length_); | 354 text_filter_string_length_); |
| 339 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringTextfieldCleared", | 355 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringTextfieldCleared", |
| 340 num_times_textfield_cleared_); | 356 num_times_textfield_cleared_); |
| 341 if (text_filter_string_length_) { | 357 if (text_filter_string_length_) { |
| 342 UMA_HISTOGRAM_MEDIUM_TIMES( | 358 UMA_HISTOGRAM_MEDIUM_TIMES( |
| 343 "Ash.WindowSelector.TimeInOverviewWithTextFiltering", | 359 "Ash.WindowSelector.TimeInOverviewWithTextFiltering", |
| 344 base::Time::Now() - overview_start_time_); | 360 base::Time::Now() - overview_start_time_); |
| 345 UMA_HISTOGRAM_COUNTS_100( | 361 UMA_HISTOGRAM_COUNTS_100( |
| 346 "Ash.WindowSelector.ItemsWhenTextFilteringUsed", | 362 "Ash.WindowSelector.ItemsWhenTextFilteringUsed", |
| 347 remaining_items); | 363 remaining_items); |
| 348 } | 364 } |
| 349 | 365 |
| 350 // TODO(flackr): Change this to OnOverviewModeEnded and move it to when | |
| 351 // everything is done. | |
| 352 shell->OnOverviewModeEnding(); | |
| 353 | |
| 354 // Clearing the window list resets the ignored_by_shelf flag on the windows. | 366 // Clearing the window list resets the ignored_by_shelf flag on the windows. |
| 355 grid_list_.clear(); | 367 grid_list_.clear(); |
| 356 UpdateShelfVisibility(); | 368 UpdateShelfVisibility(); |
| 357 } | 369 } |
| 358 | 370 |
| 371 void WindowSelector::RemoveAllObservers() { | |
| 372 Shell* shell = Shell::GetInstance(); | |
| 373 for (std::set<aura::Window*>::iterator iter = observed_windows_.begin(); | |
|
jonross
2015/01/16 20:10:20
for(auto)
bruthig
2015/01/16 20:22:46
Done.
| |
| 374 iter != observed_windows_.end(); ++iter) { | |
| 375 (*iter)->RemoveObserver(this); | |
| 376 } | |
| 377 shell->activation_client()->RemoveObserver(this); | |
| 378 shell->GetScreen()->RemoveObserver(this); | |
| 379 if (restore_focus_window_) | |
| 380 restore_focus_window_->RemoveObserver(this); | |
| 381 } | |
| 382 | |
| 359 void WindowSelector::CancelSelection() { | 383 void WindowSelector::CancelSelection() { |
| 360 delegate_->OnSelectionEnded(); | 384 delegate_->OnSelectionEnded(); |
| 361 } | 385 } |
| 362 | 386 |
| 363 void WindowSelector::OnGridEmpty(WindowGrid* grid) { | 387 void WindowSelector::OnGridEmpty(WindowGrid* grid) { |
| 364 ScopedVector<WindowGrid>::iterator iter = | 388 ScopedVector<WindowGrid>::iterator iter = |
| 365 std::find(grid_list_.begin(), grid_list_.end(), grid); | 389 std::find(grid_list_.begin(), grid_list_.end(), grid); |
| 366 DCHECK(iter != grid_list_.end()); | 390 DCHECK(iter != grid_list_.end()); |
| 367 grid_list_.erase(iter); | 391 grid_list_.erase(iter); |
| 368 // TODO(flackr): Use the previous index for more than two displays. | 392 // TODO(flackr): Use the previous index for more than two displays. |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 402 if (!grid_list_[selected_grid_index_]->is_selecting()) | 426 if (!grid_list_[selected_grid_index_]->is_selecting()) |
| 403 return false; | 427 return false; |
| 404 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.ArrowKeyPresses", | 428 UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.ArrowKeyPresses", |
| 405 num_key_presses_); | 429 num_key_presses_); |
| 406 UMA_HISTOGRAM_CUSTOM_COUNTS( | 430 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 407 "Ash.WindowSelector.KeyPressesOverItemsRatio", | 431 "Ash.WindowSelector.KeyPressesOverItemsRatio", |
| 408 (num_key_presses_ * 100) / num_items_, 1, 300, 30); | 432 (num_key_presses_ * 100) / num_items_, 1, 300, 30); |
| 409 Shell::GetInstance()->metrics()->RecordUserMetricsAction( | 433 Shell::GetInstance()->metrics()->RecordUserMetricsAction( |
| 410 UMA_WINDOW_OVERVIEW_ENTER_KEY); | 434 UMA_WINDOW_OVERVIEW_ENTER_KEY); |
| 411 wm::GetWindowState(grid_list_[selected_grid_index_]-> | 435 wm::GetWindowState(grid_list_[selected_grid_index_]-> |
| 412 SelectedWindow()->SelectionWindow())->Activate(); | 436 SelectedWindow()->GetWindow())->Activate(); |
| 413 break; | 437 break; |
| 414 default: | 438 default: |
| 415 // Not a key we are interested in, allow the textfield to handle it. | 439 // Not a key we are interested in, allow the textfield to handle it. |
| 416 return false; | 440 return false; |
| 417 } | 441 } |
| 418 return true; | 442 return true; |
| 419 } | 443 } |
| 420 | 444 |
| 421 void WindowSelector::OnDisplayAdded(const gfx::Display& display) { | 445 void WindowSelector::OnDisplayAdded(const gfx::Display& display) { |
| 422 } | 446 } |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 RootWindowGridComparator(gained_active->GetRootWindow())); | 491 RootWindowGridComparator(gained_active->GetRootWindow())); |
| 468 if (grid == grid_list_.end()) | 492 if (grid == grid_list_.end()) |
| 469 return; | 493 return; |
| 470 const std::vector<WindowSelectorItem*> windows = (*grid)->window_list(); | 494 const std::vector<WindowSelectorItem*> windows = (*grid)->window_list(); |
| 471 | 495 |
| 472 ScopedVector<WindowSelectorItem>::const_iterator iter = std::find_if( | 496 ScopedVector<WindowSelectorItem>::const_iterator iter = std::find_if( |
| 473 windows.begin(), windows.end(), | 497 windows.begin(), windows.end(), |
| 474 WindowSelectorItemTargetComparator(gained_active)); | 498 WindowSelectorItemTargetComparator(gained_active)); |
| 475 | 499 |
| 476 if (iter != windows.end()) | 500 if (iter != windows.end()) |
| 477 (*iter)->RestoreWindowOnExit(gained_active); | 501 (*iter)->ShowWindowOnExit(); |
| 478 | 502 |
| 479 // Don't restore focus on exit if a window was just activated. | 503 // Don't restore focus on exit if a window was just activated. |
| 480 ResetFocusRestoreWindow(false); | 504 ResetFocusRestoreWindow(false); |
| 481 CancelSelection(); | 505 CancelSelection(); |
| 482 } | 506 } |
| 483 | 507 |
| 484 void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active, | 508 void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active, |
| 485 aura::Window* actual_active) { | 509 aura::Window* actual_active) { |
| 486 OnWindowActivated(request_active, actual_active); | 510 OnWindowActivated(request_active, actual_active); |
| 487 } | 511 } |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 595 for (size_t i = 0; | 619 for (size_t i = 0; |
| 596 i <= grid_list_.size() && | 620 i <= grid_list_.size() && |
| 597 grid_list_[selected_grid_index_]->Move(direction, animate); i++) { | 621 grid_list_[selected_grid_index_]->Move(direction, animate); i++) { |
| 598 // TODO(flackr): If there are more than two monitors, move between grids | 622 // TODO(flackr): If there are more than two monitors, move between grids |
| 599 // in the requested direction. | 623 // in the requested direction. |
| 600 selected_grid_index_ = (selected_grid_index_ + 1) % grid_list_.size(); | 624 selected_grid_index_ = (selected_grid_index_ + 1) % grid_list_.size(); |
| 601 } | 625 } |
| 602 } | 626 } |
| 603 | 627 |
| 604 } // namespace ash | 628 } // namespace ash |
| OLD | NEW |