// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/exo/data_device.h" #include "base/logging.h" #include "components/exo/data_device_delegate.h" #include "components/exo/data_offer.h" #include "components/exo/seat.h" #include "components/exo/surface.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard_monitor.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drop_target_event.h" namespace exo { class ScopedDataOffer { public: ScopedDataOffer(DataOffer* data_offer, DataOfferObserver* observer) : data_offer_(data_offer), observer_(observer) { data_offer_->AddObserver(observer_); } ~ScopedDataOffer() { data_offer_->RemoveObserver(observer_); } DataOffer* get() { return data_offer_; } private: DataOffer* const data_offer_; DataOfferObserver* const observer_; DISALLOW_COPY_AND_ASSIGN(ScopedDataOffer); }; class ScopedSurface { public: ScopedSurface(Surface* surface, SurfaceObserver* observer) : surface_(surface), observer_(observer) { surface_->AddSurfaceObserver(observer_); } ~ScopedSurface() { surface_->RemoveSurfaceObserver(observer_); } Surface* get() { return surface_; } private: Surface* const surface_; SurfaceObserver* const observer_; DISALLOW_COPY_AND_ASSIGN(ScopedSurface); }; DataDevice::DataDevice(DataDeviceDelegate* delegate, Seat* seat, FileHelper* file_helper) : delegate_(delegate), seat_(seat), file_helper_(file_helper) { WMHelper::GetInstance()->AddDragDropObserver(this); ui::ClipboardMonitor::GetInstance()->AddObserver(this); seat_->AddObserver(this); OnSurfaceFocusing(seat_->GetFocusedSurface()); } DataDevice::~DataDevice() { delegate_->OnDataDeviceDestroying(this); WMHelper::GetInstance()->RemoveDragDropObserver(this); ui::ClipboardMonitor::GetInstance()->RemoveObserver(this); seat_->RemoveObserver(this); } void DataDevice::StartDrag(const DataSource* source_resource, Surface* origin_resource, Surface* icon_resource, uint32_t serial) { // TODO(hirono): Check if serial is valid. crbug.com/746111 NOTIMPLEMENTED(); } void DataDevice::SetSelection(DataSource* source, uint32_t serial) { // TODO(hirono): Check if serial is valid. crbug.com/746111 seat_->SetSelection(source); } void DataDevice::OnDragEntered(const ui::DropTargetEvent& event) { DCHECK(!data_offer_); Surface* surface = GetEffectiveTargetForEvent(event); if (!surface) return; base::flat_set dnd_actions; if (event.source_operations() & ui::DragDropTypes::DRAG_MOVE) { dnd_actions.insert(DndAction::kMove); } if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) { dnd_actions.insert(DndAction::kCopy); } if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) { dnd_actions.insert(DndAction::kAsk); } data_offer_ = std::make_unique(delegate_->OnDataOffer(), this); data_offer_->get()->SetDropData(file_helper_, event.data()); data_offer_->get()->SetSourceActions(dnd_actions); data_offer_->get()->SetActions(base::flat_set(), DndAction::kAsk); delegate_->OnEnter(surface, event.location_f(), *data_offer_->get()); } int DataDevice::OnDragUpdated(const ui::DropTargetEvent& event) { if (!data_offer_) return ui::DragDropTypes::DRAG_NONE; delegate_->OnMotion(event.time_stamp(), event.location_f()); // TODO(hirono): dnd_action() here may not be updated. Chrome needs to provide // a way to update DND action asynchronously. switch (data_offer_->get()->dnd_action()) { case DndAction::kMove: return ui::DragDropTypes::DRAG_MOVE; case DndAction::kCopy: return ui::DragDropTypes::DRAG_COPY; case DndAction::kAsk: return ui::DragDropTypes::DRAG_LINK; case DndAction::kNone: return ui::DragDropTypes::DRAG_NONE; } } void DataDevice::OnDragExited() { if (!data_offer_) return; delegate_->OnLeave(); data_offer_.reset(); } int DataDevice::OnPerformDrop(const ui::DropTargetEvent& event) { if (!data_offer_) return ui::DragDropTypes::DRAG_NONE; delegate_->OnDrop(); data_offer_.reset(); return ui::DragDropTypes::DRAG_NONE; } void DataDevice::OnClipboardDataChanged() { if (!focused_surface_) return; SetSelectionToCurrentClipboardData(); } void DataDevice::OnSurfaceFocusing(Surface* surface) { Surface* next_focused_surface = surface && delegate_->CanAcceptDataEventsForSurface(surface) ? surface : nullptr; // Check if focused surface is not changed. if (focused_surface_ && focused_surface_->get() == next_focused_surface) return; std::unique_ptr last_focused_surface = std::move(focused_surface_); focused_surface_ = next_focused_surface ? std::make_unique( next_focused_surface, this) : nullptr; // Check if the client newly obtained focus. if (focused_surface_ && !last_focused_surface) SetSelectionToCurrentClipboardData(); } void DataDevice::OnSurfaceFocused(Surface* surface) {} void DataDevice::OnDataOfferDestroying(DataOffer* data_offer) { if (data_offer_ && data_offer_->get() == data_offer) data_offer_.reset(); } void DataDevice::OnSurfaceDestroying(Surface* surface) { if (focused_surface_ && focused_surface_->get() == surface) focused_surface_.reset(); } Surface* DataDevice::GetEffectiveTargetForEvent( const ui::DropTargetEvent& event) const { aura::Window* window = static_cast(event.target()); if (!window) return nullptr; Surface* target = Surface::AsSurface(window); if (!target) return nullptr; return delegate_->CanAcceptDataEventsForSurface(target) ? target : nullptr; } void DataDevice::SetSelectionToCurrentClipboardData() { DataOffer* data_offer = delegate_->OnDataOffer(); data_offer->SetClipboardData(file_helper_, *ui::Clipboard::GetForCurrentThread()); delegate_->OnSelection(*data_offer); } } // namespace exo