Home | History | Annotate | Download | only in menu
      1 // Copyright 2014 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 "ui/views/controls/menu/menu_message_loop_aura.h"
      6 
      7 #if defined(OS_WIN)
      8 #include <windowsx.h>
      9 #endif
     10 
     11 #include "base/run_loop.h"
     12 #include "ui/aura/client/screen_position_client.h"
     13 #include "ui/aura/env.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/aura/window_event_dispatcher.h"
     16 #include "ui/aura/window_tree_host.h"
     17 #include "ui/events/event.h"
     18 #include "ui/events/platform/platform_event_source.h"
     19 #include "ui/events/platform/scoped_event_dispatcher.h"
     20 #include "ui/views/controls/menu/menu_controller.h"
     21 #include "ui/views/widget/widget.h"
     22 #include "ui/wm/public/activation_change_observer.h"
     23 #include "ui/wm/public/activation_client.h"
     24 #include "ui/wm/public/dispatcher_client.h"
     25 #include "ui/wm/public/drag_drop_client.h"
     26 
     27 #if defined(OS_WIN)
     28 #include "ui/base/win/internal_constants.h"
     29 #include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h"
     30 #include "ui/views/win/hwnd_util.h"
     31 #else
     32 #include "ui/views/controls/menu/menu_event_dispatcher_linux.h"
     33 #endif
     34 
     35 using aura::client::ScreenPositionClient;
     36 
     37 namespace views {
     38 
     39 namespace {
     40 
     41 aura::Window* GetOwnerRootWindow(views::Widget* owner) {
     42   return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL;
     43 }
     44 
     45 // ActivationChangeObserverImpl is used to observe activation changes and close
     46 // the menu. Additionally it listens for the root window to be destroyed and
     47 // cancel the menu as well.
     48 class ActivationChangeObserverImpl
     49     : public aura::client::ActivationChangeObserver,
     50       public aura::WindowObserver,
     51       public ui::EventHandler {
     52  public:
     53   ActivationChangeObserverImpl(MenuController* controller, aura::Window* root)
     54       : controller_(controller), root_(root) {
     55     aura::client::GetActivationClient(root_)->AddObserver(this);
     56     root_->AddObserver(this);
     57     root_->AddPreTargetHandler(this);
     58   }
     59 
     60   virtual ~ActivationChangeObserverImpl() { Cleanup(); }
     61 
     62   // aura::client::ActivationChangeObserver:
     63   virtual void OnWindowActivated(aura::Window* gained_active,
     64                                  aura::Window* lost_active) OVERRIDE {
     65     if (!controller_->drag_in_progress())
     66       controller_->CancelAll();
     67   }
     68 
     69   // aura::WindowObserver:
     70   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { Cleanup(); }
     71 
     72   // ui::EventHandler:
     73   virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE {
     74     controller_->CancelAll();
     75   }
     76 
     77  private:
     78   void Cleanup() {
     79     if (!root_)
     80       return;
     81     // The ActivationClient may have been destroyed by the time we get here.
     82     aura::client::ActivationClient* client =
     83         aura::client::GetActivationClient(root_);
     84     if (client)
     85       client->RemoveObserver(this);
     86     root_->RemovePreTargetHandler(this);
     87     root_->RemoveObserver(this);
     88     root_ = NULL;
     89   }
     90 
     91   MenuController* controller_;
     92   aura::Window* root_;
     93 
     94   DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl);
     95 };
     96 
     97 }  // namespace
     98 
     99 // static
    100 MenuMessageLoop* MenuMessageLoop::Create() {
    101   return new MenuMessageLoopAura;
    102 }
    103 
    104 MenuMessageLoopAura::MenuMessageLoopAura() : owner_(NULL) {
    105 }
    106 
    107 MenuMessageLoopAura::~MenuMessageLoopAura() {
    108 }
    109 
    110 void MenuMessageLoopAura::RepostEventToWindow(const ui::LocatedEvent& event,
    111                                               gfx::NativeWindow window,
    112                                               const gfx::Point& screen_loc) {
    113   aura::Window* root = window->GetRootWindow();
    114   ScreenPositionClient* spc = aura::client::GetScreenPositionClient(root);
    115   if (!spc)
    116     return;
    117 
    118   gfx::Point root_loc(screen_loc);
    119   spc->ConvertPointFromScreen(root, &root_loc);
    120 
    121   ui::MouseEvent clone(static_cast<const ui::MouseEvent&>(event));
    122   clone.set_location(root_loc);
    123   clone.set_root_location(root_loc);
    124   root->GetHost()->dispatcher()->RepostEvent(clone);
    125 }
    126 
    127 void MenuMessageLoopAura::Run(MenuController* controller,
    128                               Widget* owner,
    129                               bool nested_menu) {
    130   // |owner_| may be NULL.
    131   owner_ = owner;
    132   aura::Window* root = GetOwnerRootWindow(owner_);
    133   // It is possible for the same MenuMessageLoopAura to start a nested
    134   // message-loop while it is already running a nested loop. So make sure the
    135   // quit-closure gets reset to the outer loop's quit-closure once the innermost
    136   // loop terminates.
    137   base::AutoReset<base::Closure> reset_quit_closure(&message_loop_quit_,
    138                                                     base::Closure());
    139 
    140 #if defined(OS_WIN)
    141   internal::MenuMessagePumpDispatcher nested_dispatcher(controller);
    142   if (root) {
    143     scoped_ptr<ActivationChangeObserverImpl> observer;
    144     if (!nested_menu)
    145       observer.reset(new ActivationChangeObserverImpl(controller, root));
    146     aura::client::DispatcherRunLoop run_loop(
    147         aura::client::GetDispatcherClient(root), &nested_dispatcher);
    148     message_loop_quit_ = run_loop.QuitClosure();
    149     run_loop.Run();
    150   } else {
    151     base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    152     base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    153     base::RunLoop run_loop(&nested_dispatcher);
    154     message_loop_quit_ = run_loop.QuitClosure();
    155     run_loop.Run();
    156   }
    157 #else
    158   internal::MenuEventDispatcher event_dispatcher(controller);
    159   scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
    160       nested_dispatcher_.Pass();
    161   if (ui::PlatformEventSource::GetInstance()) {
    162     nested_dispatcher_ =
    163         ui::PlatformEventSource::GetInstance()->OverrideDispatcher(
    164             &event_dispatcher);
    165   }
    166   if (root) {
    167     scoped_ptr<ActivationChangeObserverImpl> observer;
    168     if (!nested_menu)
    169       observer.reset(new ActivationChangeObserverImpl(controller, root));
    170     aura::client::DispatcherRunLoop run_loop(
    171         aura::client::GetDispatcherClient(root), NULL);
    172     message_loop_quit_ = run_loop.QuitClosure();
    173     run_loop.Run();
    174   } else {
    175     base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    176     base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    177     base::RunLoop run_loop;
    178     message_loop_quit_ = run_loop.QuitClosure();
    179     run_loop.Run();
    180   }
    181   nested_dispatcher_ = old_dispatcher.Pass();
    182 #endif
    183 }
    184 
    185 bool MenuMessageLoopAura::ShouldQuitNow() const {
    186   aura::Window* root = GetOwnerRootWindow(owner_);
    187   return !aura::client::GetDragDropClient(root) ||
    188          !aura::client::GetDragDropClient(root)->IsDragDropInProgress();
    189 }
    190 
    191 void MenuMessageLoopAura::QuitNow() {
    192   CHECK(!message_loop_quit_.is_null());
    193   message_loop_quit_.Run();
    194   // Restore the previous dispatcher.
    195   nested_dispatcher_.reset();
    196 }
    197 
    198 void MenuMessageLoopAura::ClearOwner() {
    199   owner_ = NULL;
    200 }
    201 
    202 }  // namespace views
    203