| // Copyright (c) 2011 The Chromium OS 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 "window_manager/panels/panel_manager.h" |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "cros/chromeos_wm_ipc_enums.h" |
| #include "window_manager/atom_cache.h" |
| #include "window_manager/event_consumer_registrar.h" |
| #include "window_manager/motion_event_coalescer.h" |
| #include "window_manager/panels/panel.h" |
| #include "window_manager/panels/panel_bar.h" |
| #include "window_manager/panels/panel_dock.h" |
| #include "window_manager/panels/panel_container.h" |
| #include "window_manager/stacking_manager.h" |
| #include "window_manager/window.h" |
| #include "window_manager/window_manager.h" |
| |
| using std::make_pair; |
| using std::map; |
| using std::pair; |
| using std::set; |
| using std::tr1::shared_ptr; |
| using std::vector; |
| using window_manager::util::FindWithDefault; |
| using window_manager::util::XidStr; |
| |
| namespace window_manager { |
| |
| // Frequency with which we should update the position of dragged panels. |
| static const int kDraggedPanelUpdateMs = 25; |
| |
| // How long should the animation when detaching panels from containers take? |
| static const int kDetachPanelAnimMs = 100; |
| |
| // Chosen because 1280 - 256 = 1024. |
| const int PanelManager::kPanelDockWidth = 256; |
| |
| PanelManager::PanelManager(WindowManager* wm) |
| : wm_(wm), |
| dragged_panel_(NULL), |
| fullscreen_panel_(NULL), |
| dragged_panel_event_coalescer_( |
| new MotionEventCoalescer( |
| wm_->event_loop(), |
| NewPermanentCallback( |
| this, &PanelManager::HandlePeriodicPanelDragMotion), |
| kDraggedPanelUpdateMs)), |
| panel_bar_(new PanelBar(this)), |
| left_panel_dock_( |
| new PanelDock(this, PanelDock::DOCK_TYPE_LEFT, kPanelDockWidth)), |
| right_panel_dock_( |
| new PanelDock(this, PanelDock::DOCK_TYPE_RIGHT, kPanelDockWidth)), |
| saw_map_request_(false), |
| event_consumer_registrar_(new EventConsumerRegistrar(wm_, this)) { |
| event_consumer_registrar_->RegisterForChromeMessages( |
| chromeos::WM_IPC_MESSAGE_WM_SET_PANEL_STATE); |
| event_consumer_registrar_->RegisterForChromeMessages( |
| chromeos::WM_IPC_MESSAGE_WM_NOTIFY_PANEL_DRAGGED); |
| event_consumer_registrar_->RegisterForChromeMessages( |
| chromeos::WM_IPC_MESSAGE_WM_NOTIFY_PANEL_DRAG_COMPLETE); |
| |
| wm_->focus_manager()->RegisterFocusChangeListener(this); |
| |
| RegisterContainer(panel_bar_.get()); |
| RegisterContainer(left_panel_dock_.get()); |
| RegisterContainer(right_panel_dock_.get()); |
| } |
| |
| PanelManager::~PanelManager() { |
| wm_->focus_manager()->UnregisterFocusChangeListener(this); |
| dragged_panel_ = NULL; |
| } |
| |
| bool PanelManager::IsInputWindow(XWindow xid) { |
| return container_input_xids_.count(xid) || panel_input_xids_.count(xid); |
| } |
| |
| void PanelManager::HandleScreenResize() { |
| for (vector<PanelContainer*>::iterator it = containers_.begin(); |
| it != containers_.end(); ++it) { |
| (*it)->HandleScreenResize(); |
| } |
| for (PanelMap::iterator it = panels_.begin(); it != panels_.end(); ++it) |
| it->second->HandleScreenResize(); |
| } |
| |
| bool PanelManager::HandleWindowMapRequest(Window* win) { |
| saw_map_request_ = true; |
| |
| if (win->type() != chromeos::WM_IPC_WINDOW_CHROME_PANEL_CONTENT && |
| win->type() != chromeos::WM_IPC_WINDOW_CHROME_PANEL_TITLEBAR && |
| (!win->transient_for_xid() || !GetPanelByXid(win->transient_for_xid()))) |
| return false; |
| |
| DoInitialSetupForWindow(win); |
| return true; |
| } |
| |
| void PanelManager::HandleWindowMap(Window* win) { |
| if (win->override_redirect()) |
| return; |
| |
| if (win->type() != chromeos::WM_IPC_WINDOW_CHROME_PANEL_CONTENT && |
| win->type() != chromeos::WM_IPC_WINDOW_CHROME_PANEL_TITLEBAR) { |
| // The only non-panel windows that we'll handle are transients |
| // belonging to panels. |
| if (!win->transient_for_xid()) |
| return; |
| |
| Panel* owner_panel = GetPanelByXid(win->transient_for_xid()); |
| if (!owner_panel) { |
| // Maybe its owner is itself a transient for a panel. |
| Window* owner_win = wm_->GetWindow(win->transient_for_xid()); |
| if (owner_win) |
| owner_panel = GetPanelOwningTransientWindow(*owner_win); |
| } |
| if (owner_panel) { |
| transient_xids_to_owners_[win->xid()] = owner_panel; |
| owner_panel->HandleTransientWindowMap(win); |
| if (!win->is_rgba()) |
| win->SetShadowType(Shadow::TYPE_RECTANGULAR); |
| } |
| return; |
| } |
| |
| // Handle initial setup for existing windows for which we never saw a map |
| // request event. |
| if (!saw_map_request_) |
| DoInitialSetupForWindow(win); |
| |
| switch (win->type()) { |
| case chromeos::WM_IPC_WINDOW_CHROME_PANEL_TITLEBAR: |
| // Don't do anything with panel titlebars when they're first |
| // mapped; we'll handle them after we see the corresponding content |
| // window. |
| break; |
| |
| case chromeos::WM_IPC_WINDOW_CHROME_PANEL_CONTENT: { |
| // Resize the titlebar to match the content window's current width. |
| // There's a small chance that we'll constrain the content size in the |
| // Panel c'tor, but this lets us get the titlebar to the right size |
| // before we make both windows visible in the common case. |
| if (!win->type_params().empty()) { |
| Window* titlebar_win = wm_->GetWindow(win->type_params().at(0)); |
| if (titlebar_win) { |
| titlebar_win->Resize( |
| Size(win->client_width(), titlebar_win->client_height()), |
| GRAVITY_NORTHWEST); |
| } |
| } |
| |
| if (win->has_initial_pixmap()) { |
| HandleContentWindowInitialPixmapFetch(win); |
| } else { |
| // If we don't have the window's pixmap yet, defer creating the Panel |
| // object and register to get notified after the pixmap is loaded. |
| event_consumer_registrar_->RegisterForWindowEvents(win->xid()); |
| content_xids_without_initial_pixmaps_.insert(win->xid()); |
| } |
| break; |
| } |
| |
| default: |
| NOTREACHED() << "Unhandled window type " << win->type(); |
| } |
| } |
| |
| void PanelManager::HandleWindowUnmap(Window* win) { |
| if (win->override_redirect()) |
| return; |
| |
| set<XWindow>::iterator content_it = |
| content_xids_without_initial_pixmaps_.find(win->xid()); |
| if (content_it != content_xids_without_initial_pixmaps_.end()) { |
| event_consumer_registrar_->UnregisterForWindowEvents(win->xid()); |
| content_xids_without_initial_pixmaps_.erase(content_it); |
| } |
| |
| Panel* owner_panel = GetPanelOwningTransientWindow(*win); |
| if (owner_panel) { |
| transient_xids_to_owners_.erase(win->xid()); |
| owner_panel->HandleTransientWindowUnmap(win); |
| return; |
| } |
| |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) |
| return; |
| |
| PanelContainer* container = GetContainerForPanel(*panel); |
| if (container) |
| RemovePanelFromContainer(panel, container); |
| if (panel == dragged_panel_) |
| HandlePanelDragComplete(panel, true); // removed=true |
| if (panel == fullscreen_panel_) |
| fullscreen_panel_ = NULL; |
| |
| // If the panel was focused, assign the focus to another panel, or |
| // failing that, let the window manager decide what to do with it. |
| if (panel->is_focused()) { |
| XTime timestamp = wm()->GetCurrentTimeFromServer(); |
| if (!TakeFocus(timestamp)) |
| wm_->TakeFocus(timestamp); |
| } |
| |
| vector<XWindow> input_windows; |
| panel->GetInputWindows(&input_windows); |
| for (vector<XWindow>::const_iterator it = input_windows.begin(); |
| it != input_windows.end(); ++it) { |
| CHECK(panel_input_xids_.erase(*it) == 1); |
| } |
| |
| // Clean up any references to this panel in the transient window map. |
| vector<XWindow> orphaned_transient_xids; |
| for (map<XWindow, Panel*>::const_iterator it = |
| transient_xids_to_owners_.begin(); |
| it != transient_xids_to_owners_.end(); ++it) { |
| if (it->second == panel) |
| orphaned_transient_xids.push_back(it->first); |
| } |
| for (vector<XWindow>::const_iterator it = orphaned_transient_xids.begin(); |
| it != orphaned_transient_xids.end(); ++it) { |
| CHECK(transient_xids_to_owners_.erase(*it) == 1); |
| } |
| |
| CHECK(panels_by_titlebar_xid_.erase(panel->titlebar_xid()) == 1); |
| CHECK(panels_.erase(panel->content_xid()) == 1); |
| } |
| |
| void PanelManager::HandleWindowPixmapFetch(Window* win) { |
| set<XWindow>::iterator it = |
| content_xids_without_initial_pixmaps_.find(win->xid()); |
| if (it != content_xids_without_initial_pixmaps_.end()) { |
| event_consumer_registrar_->UnregisterForWindowEvents(win->xid()); |
| content_xids_without_initial_pixmaps_.erase(it); |
| HandleContentWindowInitialPixmapFetch(win); |
| } |
| } |
| |
| void PanelManager::HandleWindowConfigureRequest(Window* win, |
| const Rect& requested_bounds) { |
| if (Panel* owner_panel = GetPanelOwningTransientWindow(*win)) { |
| owner_panel->HandleTransientWindowConfigureRequest(win, requested_bounds); |
| return; |
| } |
| |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) |
| return; |
| |
| if (win != panel->content_win()) { |
| LOG(WARNING) << "Ignoring request to configure non-content window " |
| << win->xid_str() << " for panel " << panel->xid_str(); |
| return; |
| } |
| PanelContainer* container = GetContainerForPanel(*panel); |
| if (!container) { |
| LOG(WARNING) << "Ignoring request to configure panel " << panel->xid_str() |
| << " while it's not in a container"; |
| return; |
| } |
| if (panel->is_being_resized_by_user()) { |
| LOG(WARNING) << "Ignoring request to configure panel " << panel->xid_str() |
| << " while it's being manually resized"; |
| win->SendSyntheticConfigureNotify(); |
| return; |
| } |
| |
| if (requested_bounds.size() != panel->content_size()) |
| container->HandlePanelResizeRequest(panel, requested_bounds.size()); |
| else |
| win->SendSyntheticConfigureNotify(); |
| } |
| |
| void PanelManager::HandleButtonPress(XWindow xid, |
| const Point& relative_pos, |
| const Point& absolute_pos, |
| int button, |
| XTime timestamp) { |
| // If this is a container's input window, notify the container. |
| PanelContainer* container = GetContainerOwningInputWindow(xid); |
| if (container) { |
| container->HandleInputWindowButtonPress( |
| xid, relative_pos, absolute_pos, button, timestamp); |
| return; |
| } |
| |
| // If this is a panel's input window, notify the panel. |
| Panel* panel = GetPanelOwningInputWindow(xid); |
| if (panel) { |
| panel->HandleInputWindowButtonPress(xid, relative_pos, button, timestamp); |
| return; |
| } |
| |
| Window* win = wm_->GetWindow(xid); |
| if (!win) |
| return; |
| |
| // If it's a panel's content window, notify the panel's container. |
| panel = GetPanelByWindow(*win); |
| if (panel) { |
| container = GetContainerForPanel(*panel); |
| if (container) |
| container->HandlePanelButtonPress(panel, button, timestamp); |
| return; |
| } |
| |
| panel = GetPanelOwningTransientWindow(*win); |
| if (panel) { |
| panel->HandleTransientWindowButtonPress(win, button, timestamp); |
| return; |
| } |
| } |
| |
| void PanelManager::HandleButtonRelease(XWindow xid, |
| const Point& relative_pos, |
| const Point& absolute_pos, |
| int button, |
| XTime timestamp) { |
| // We only care if button releases happened in container or panel input |
| // windows -- there's no current need to notify containers about button |
| // releases in their panels. |
| PanelContainer* container = GetContainerOwningInputWindow(xid); |
| if (container) { |
| container->HandleInputWindowButtonRelease( |
| xid, relative_pos, absolute_pos, button, timestamp); |
| return; |
| } |
| |
| Panel* panel = GetPanelOwningInputWindow(xid); |
| if (panel) { |
| panel->HandleInputWindowButtonRelease(xid, relative_pos, button, timestamp); |
| return; |
| } |
| } |
| |
| void PanelManager::HandlePointerEnter(XWindow xid, |
| const Point& relative_pos, |
| const Point& absolute_pos, |
| XTime timestamp) { |
| PanelContainer* container = GetContainerOwningInputWindow(xid); |
| if (container) { |
| container->HandleInputWindowPointerEnter( |
| xid, relative_pos, absolute_pos, timestamp); |
| return; |
| } |
| |
| // If it's a panel's titlebar window, notify the panel's container. |
| Window* win = wm_->GetWindow(xid); |
| if (win) { |
| Panel* panel = GetPanelByWindow(*win); |
| if (panel) { |
| container = GetContainerForPanel(*panel); |
| if (container && xid == panel->titlebar_xid()) |
| container->HandlePanelTitlebarPointerEnter(panel, timestamp); |
| return; |
| } |
| } |
| } |
| |
| void PanelManager::HandlePointerLeave(XWindow xid, |
| const Point& relative_pos, |
| const Point& absolute_pos, |
| XTime timestamp) { |
| PanelContainer* container = GetContainerOwningInputWindow(xid); |
| if (container) |
| container->HandleInputWindowPointerLeave( |
| xid, relative_pos, absolute_pos, timestamp); |
| } |
| |
| void PanelManager::HandlePointerMotion(XWindow xid, |
| const Point& relative_pos, |
| const Point& absolute_pos, |
| XTime timestamp) { |
| Panel* panel = GetPanelOwningInputWindow(xid); |
| if (panel) |
| panel->HandleInputWindowPointerMotion(xid, relative_pos); |
| } |
| |
| void PanelManager::HandleChromeMessage(const WmIpc::Message& msg) { |
| switch (msg.type()) { |
| case chromeos::WM_IPC_MESSAGE_WM_SET_PANEL_STATE: { |
| XWindow xid = msg.param(0); |
| Panel* panel = GetPanelByXid(xid); |
| if (!panel) { |
| LOG(WARNING) << "Ignoring WM_SET_PANEL_STATE message for non-panel " |
| << "window " << XidStr(xid); |
| return; |
| } |
| PanelContainer* container = GetContainerForPanel(*panel); |
| if (container) |
| container->HandleSetPanelStateMessage(panel, msg.param(1)); |
| break; |
| } |
| case chromeos::WM_IPC_MESSAGE_WM_NOTIFY_PANEL_DRAGGED: { |
| XWindow xid = msg.param(0); |
| Panel* panel = GetPanelByXid(xid); |
| if (!panel) { |
| LOG(WARNING) << "Ignoring WM_NOTIFY_PANEL_DRAGGED message for " |
| << "non-panel window " << XidStr(xid); |
| return; |
| } |
| if (dragged_panel_ && panel != dragged_panel_) |
| HandlePanelDragComplete(dragged_panel_, false); // removed=false |
| if (panel != dragged_panel_) { |
| dragged_panel_ = panel; |
| panel->HandleDragStart(); |
| } |
| if (!dragged_panel_event_coalescer_->IsRunning()) |
| dragged_panel_event_coalescer_->Start(); |
| // We want the right edge of the panel, but pre-IPC-version-1 Chrome |
| // sends us the left edge of the titlebar instead. |
| Point drag_pos( |
| (wm()->wm_ipc_version() >= 1) ? |
| msg.param(1) : msg.param(1) + panel->titlebar_width(), |
| msg.param(2)); |
| dragged_panel_event_coalescer_->StorePosition(drag_pos); |
| break; |
| } |
| case chromeos::WM_IPC_MESSAGE_WM_NOTIFY_PANEL_DRAG_COMPLETE: { |
| XWindow xid = msg.param(0); |
| Panel* panel = GetPanelByXid(xid); |
| if (!panel) { |
| LOG(WARNING) << "Ignoring WM_NOTIFY_PANEL_DRAG_COMPLETE message for " |
| << "non-panel window " << XidStr(xid); |
| return; |
| } |
| HandlePanelDragComplete(panel, false); // removed=false |
| break; |
| } |
| default: |
| return; |
| } |
| } |
| |
| void PanelManager::HandleClientMessage(XWindow xid, |
| XAtom message_type, |
| const long data[5]) { |
| Window* win = wm_->GetWindow(xid); |
| if (!win) |
| return; |
| |
| if (Panel* owner_panel = GetPanelOwningTransientWindow(*win)) { |
| owner_panel->HandleTransientWindowClientMessage(win, message_type, data); |
| return; |
| } |
| |
| Panel* panel = GetPanelByXid(xid); |
| if (!panel) |
| return; |
| |
| if (message_type == wm_->GetXAtom(ATOM_NET_ACTIVE_WINDOW)) { |
| DLOG(INFO) << "Got _NET_ACTIVE_WINDOW request to focus " << XidStr(xid) |
| << " (requestor says its currently-active window is " |
| << XidStr(data[2]) << "; real active window is " |
| << XidStr(wm_->active_window_xid()) << ")"; |
| if (PanelContainer* container = GetContainerForPanel(*panel)) |
| container->HandleFocusPanelMessage(panel, data[1]); |
| } else if (message_type == wm_->GetXAtom(ATOM_NET_WM_STATE)) { |
| if (panel->content_xid() == xid) { |
| map<XAtom, bool> states; |
| panel->content_win()->ParseWmStateMessage(data, &states); |
| map<XAtom, bool>::const_iterator it = |
| states.find(wm_->GetXAtom(ATOM_NET_WM_STATE_FULLSCREEN)); |
| if (it != states.end()) { |
| bool fullscreen = it->second; |
| DLOG(INFO) << "Panel " << panel->xid_str() << " " |
| << (fullscreen ? "set" : "unset") << " its fullscreen hint"; |
| if (fullscreen) |
| MakePanelFullscreen(panel); |
| else |
| RestoreFullscreenPanel(panel); |
| } |
| } |
| } |
| } |
| |
| void PanelManager::HandleWindowPropertyChange(XWindow xid, XAtom xatom) { |
| Window* win = wm_->GetWindowOrDie(xid); |
| Panel* panel = GetPanelByWindow(*win); |
| DCHECK(panel) << "Got property change for non-panel window " << XidStr(xid); |
| if (!panel || panel->content_win() != win) |
| return; |
| |
| if (xatom == wm_->GetXAtom(ATOM_WM_HINTS)) { |
| if (win->wm_hint_urgent() != panel->is_urgent()) { |
| panel->set_is_urgent(win->wm_hint_urgent()); |
| PanelContainer* container = GetContainerForPanel(*panel); |
| if (container) |
| container->HandlePanelUrgencyChange(panel); |
| } |
| } else if (xatom == wm_->GetXAtom(ATOM_WM_NORMAL_HINTS)) { |
| if (win == panel->content_win()) |
| panel->HandleContentWindowSizeHintsChange(); |
| } else { |
| LOG(DFATAL) << "Got unexpected property " << wm_->GetXAtomName(xatom) |
| << " (" << XidStr(xatom) << ") change for " << win->xid_str(); |
| } |
| } |
| |
| void PanelManager::HandleFocusChange() { |
| // If a fullscreen panel loses the focus, un-fullscreen it. |
| if (fullscreen_panel_ && !fullscreen_panel_->is_focused()) |
| RestoreFullscreenPanel(fullscreen_panel_); |
| } |
| |
| void PanelManager::RegisterAreaChangeListener( |
| PanelManagerAreaChangeListener* listener) { |
| DCHECK(listener); |
| bool added = area_change_listeners_.insert(listener).second; |
| DCHECK(added) << "Listener " << listener << " was already registered"; |
| } |
| |
| void PanelManager::UnregisterAreaChangeListener( |
| PanelManagerAreaChangeListener* listener) { |
| int num_removed = area_change_listeners_.erase(listener); |
| DCHECK_EQ(num_removed, 1) << "Listener " << listener << " wasn't registered"; |
| } |
| |
| void PanelManager::GetArea(int* left_width, int* right_width) const { |
| DCHECK(left_width); |
| DCHECK(right_width); |
| *left_width = |
| left_panel_dock_->is_visible() ? left_panel_dock_->width() : 0; |
| *right_width = |
| right_panel_dock_->is_visible() ? right_panel_dock_->width() : 0; |
| } |
| |
| void PanelManager::HandlePanelResizeByUser(Panel* panel) { |
| DCHECK(panel); |
| if (PanelContainer* container = GetContainerForPanel(*panel)) |
| container->HandlePanelResizeByUser(panel); |
| } |
| |
| void PanelManager::HandleDockVisibilityChange(PanelDock* dock) { |
| for (set<PanelManagerAreaChangeListener*>::const_iterator it = |
| area_change_listeners_.begin(); |
| it != area_change_listeners_.end(); ++it) { |
| (*it)->HandlePanelManagerAreaChange(); |
| } |
| } |
| |
| bool PanelManager::TakeFocus(XTime timestamp) { |
| return panel_bar_->TakeFocus(timestamp) || |
| left_panel_dock_->TakeFocus(timestamp) || |
| right_panel_dock_->TakeFocus(timestamp); |
| } |
| |
| Panel* PanelManager::GetPanelByXid(XWindow xid) { |
| Window* win = wm_->GetWindow(xid); |
| if (!win) |
| return NULL; |
| return GetPanelByWindow(*win); |
| } |
| |
| Panel* PanelManager::GetPanelByWindow(const Window& win) { |
| shared_ptr<Panel> panel = FindWithDefault( |
| panels_, win.xid(), shared_ptr<Panel>()); |
| if (panel.get()) |
| return panel.get(); |
| |
| return FindWithDefault( |
| panels_by_titlebar_xid_, win.xid(), static_cast<Panel*>(NULL)); |
| } |
| |
| Panel* PanelManager::GetPanelOwningTransientWindow(const Window& win) { |
| return FindWithDefault(transient_xids_to_owners_, |
| win.xid(), |
| static_cast<Panel*>(NULL)); |
| } |
| |
| Panel* PanelManager::GetPanelOwningInputWindow(XWindow xid) { |
| return FindWithDefault(panel_input_xids_, xid, static_cast<Panel*>(NULL)); |
| } |
| |
| PanelContainer* PanelManager::GetContainerOwningInputWindow(XWindow xid) { |
| return FindWithDefault( |
| container_input_xids_, xid, static_cast<PanelContainer*>(NULL)); |
| } |
| |
| void PanelManager::RegisterContainer(PanelContainer* container) { |
| vector<XWindow> input_xids; |
| container->GetInputWindows(&input_xids); |
| for (vector<XWindow>::const_iterator it = input_xids.begin(); |
| it != input_xids.end(); ++it) { |
| DLOG(INFO) << "Registering input window " << XidStr(*it) |
| << " for container " << container; |
| CHECK(container_input_xids_.insert(make_pair(*it, container)).second); |
| } |
| containers_.push_back(container); |
| } |
| |
| void PanelManager::DoInitialSetupForWindow(Window* win) { |
| win->SetVisibility(Window::VISIBILITY_HIDDEN); |
| } |
| |
| void PanelManager::HandleContentWindowInitialPixmapFetch(Window* win) { |
| if (win->type_params().empty()) { |
| LOG(WARNING) << "Panel " << win->xid_str() << " is missing type " |
| << "parameter for titlebar window"; |
| return; |
| } |
| Window* titlebar_win = wm_->GetWindow(win->type_params().at(0)); |
| if (!titlebar_win) { |
| LOG(WARNING) << "Unable to find titlebar " |
| << XidStr(win->type_params()[0]) |
| << " for panel " << win->xid_str(); |
| return; |
| } |
| |
| // TODO(derat): Make the second param required after Chrome has been |
| // updated. |
| bool expanded = win->type_params().size() >= 2 ? |
| win->type_params().at(1) : false; |
| DLOG(INFO) << "Adding " << (expanded ? "expanded" : "collapsed") |
| << " panel with content window " << win->xid_str() |
| << " and titlebar window " << titlebar_win->xid_str(); |
| |
| shared_ptr<Panel> panel(new Panel(this, win, titlebar_win, expanded)); |
| panel->SetTitlebarWidth(panel->content_width()); |
| |
| vector<XWindow> input_windows; |
| panel->GetInputWindows(&input_windows); |
| for (vector<XWindow>::const_iterator it = input_windows.begin(); |
| it != input_windows.end(); ++it) { |
| CHECK(panel_input_xids_.insert(make_pair(*it, panel.get())).second); |
| } |
| |
| CHECK(panels_.insert(make_pair(win->xid(), panel)).second); |
| CHECK(panels_by_titlebar_xid_.insert( |
| make_pair(titlebar_win->xid(), panel.get())).second); |
| |
| AddPanelToContainer(panel.get(), |
| panel_bar_.get(), |
| PanelContainer::PANEL_SOURCE_NEW); |
| |
| if (win->wm_state_fullscreen()) |
| MakePanelFullscreen(panel.get()); |
| } |
| |
| void PanelManager::HandlePeriodicPanelDragMotion() { |
| DCHECK(dragged_panel_); |
| if (!dragged_panel_) |
| return; |
| |
| const Point pos = dragged_panel_event_coalescer_->position(); |
| |
| bool container_handled_drag = false; |
| bool panel_was_detached = false; |
| PanelContainer* container = GetContainerForPanel(*dragged_panel_); |
| if (container) { |
| if (container->HandleNotifyPanelDraggedMessage(dragged_panel_, pos)) { |
| container_handled_drag = true; |
| } else { |
| DLOG(INFO) << "Container " << container << " told us to detach panel " |
| << dragged_panel_->xid_str() << " at " << pos; |
| RemovePanelFromContainer(dragged_panel_, container); |
| panel_was_detached = true; |
| } |
| } |
| |
| if (!container_handled_drag) { |
| if (panel_was_detached) { |
| dragged_panel_->SetTitlebarWidth(dragged_panel_->content_width()); |
| dragged_panel_->StackAtTopOfLayer(StackingManager::LAYER_DRAGGED_PANEL); |
| } |
| |
| // Offer the panel to all of the containers. If we find one that wants |
| // it, attach it; otherwise we just move the panel to the dragged location. |
| bool panel_was_reattached = false; |
| for (vector<PanelContainer*>::iterator it = containers_.begin(); |
| it != containers_.end(); ++it) { |
| if ((*it)->ShouldAddDraggedPanel(dragged_panel_, pos)) { |
| DLOG(INFO) << "Container " << *it << " told us to attach panel " |
| << dragged_panel_->xid_str() << " at " << pos; |
| AddPanelToContainer(dragged_panel_, |
| *it, |
| PanelContainer::PANEL_SOURCE_DRAGGED); |
| CHECK((*it)->HandleNotifyPanelDraggedMessage(dragged_panel_, pos)); |
| panel_was_reattached = true; |
| break; |
| } |
| } |
| if (!panel_was_reattached) { |
| dragged_panel_->Move(pos, panel_was_detached ? kDetachPanelAnimMs : 0); |
| } |
| } |
| } |
| |
| void PanelManager::HandlePanelDragComplete(Panel* panel, bool removed) { |
| DCHECK(panel); |
| DCHECK(dragged_panel_ == panel); |
| if (dragged_panel_ != panel) |
| return; |
| |
| panel->HandleDragEnd(); |
| |
| if (dragged_panel_event_coalescer_->IsRunning()) |
| dragged_panel_event_coalescer_->Stop(); |
| dragged_panel_ = NULL; |
| |
| if (!removed) { |
| PanelContainer* container = GetContainerForPanel(*panel); |
| if (container) { |
| container->HandleNotifyPanelDragCompleteMessage(panel); |
| } else { |
| DLOG(INFO) << "Attaching dropped panel " << panel->xid_str() |
| << " to panel bar"; |
| AddPanelToContainer(panel, |
| panel_bar_.get(), |
| PanelContainer::PANEL_SOURCE_DROPPED); |
| } |
| } |
| } |
| |
| void PanelManager::AddPanelToContainer(Panel* panel, |
| PanelContainer* container, |
| PanelContainer::PanelSource source) { |
| DCHECK(GetContainerForPanel(*panel) == NULL); |
| CHECK(containers_by_panel_.insert(make_pair(panel, container)).second); |
| container->AddPanel(panel, source); |
| } |
| |
| void PanelManager::RemovePanelFromContainer(Panel* panel, |
| PanelContainer* container) { |
| DCHECK(GetContainerForPanel(*panel) == container); |
| CHECK(containers_by_panel_.erase(panel) == static_cast<size_t>(1)); |
| container->RemovePanel(panel); |
| panel->SetResizable(false); |
| panel->SetShadowOpacity(1.0, kDetachPanelAnimMs); |
| panel->SetExpandedState(true); |
| } |
| |
| void PanelManager::MakePanelFullscreen(Panel* panel) { |
| DCHECK(panel); |
| if (panel->is_fullscreen()) { |
| LOG(WARNING) << "Ignoring request to fullscreen already-fullscreen " |
| << "panel " << panel->xid_str(); |
| return; |
| } |
| |
| // If there's already another fullscreen panel, unfullscreen it. |
| if (fullscreen_panel_) |
| RestoreFullscreenPanel(fullscreen_panel_); |
| DCHECK(!fullscreen_panel_); |
| |
| panel->SetFullscreenState(true); |
| fullscreen_panel_ = panel; |
| } |
| |
| void PanelManager::RestoreFullscreenPanel(Panel* panel) { |
| DCHECK(panel); |
| if (!panel->is_fullscreen()) { |
| LOG(WARNING) << "Ignoring request to restore non-fullscreen panel " |
| << panel->xid_str(); |
| return; |
| } |
| |
| panel->SetFullscreenState(false); |
| if (fullscreen_panel_ == panel) |
| fullscreen_panel_ = NULL; |
| } |
| |
| } // namespace window_manager |