root/chrome/browser/ui/gtk/panels/panel_stack_window_gtk.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Create
  2. bounds_updates_started_
  3. Close
  4. AddPanel
  5. RemovePanel
  6. MergeWith
  7. IsEmpty
  8. HasPanel
  9. MovePanelsBy
  10. BeginBatchUpdatePanelBounds
  11. AddPanelBoundsForBatchUpdate
  12. EndBatchUpdatePanelBounds
  13. IsAnimatingPanelBounds
  14. Minimize
  15. IsMinimized
  16. DrawSystemAttention
  17. OnPanelActivated
  18. ActiveWindowChanged
  19. OnWindowDeleteEvent
  20. OnWindowState
  21. EnsureWindowCreated
  22. SetStackWindowBounds

// Copyright (c) 2013 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 "chrome/browser/ui/gtk/panels/panel_stack_window_gtk.h"

#include <gdk/gdkkeysyms.h>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/panels/panel.h"
#include "chrome/browser/ui/panels/stacked_panel_collection.h"
#include "ui/base/x/active_window_watcher_x.h"

// static
NativePanelStackWindow* NativePanelStackWindow::Create(
    NativePanelStackWindowDelegate* delegate) {
  return new PanelStackWindowGtk(delegate);
}

PanelStackWindowGtk::PanelStackWindowGtk(
   NativePanelStackWindowDelegate* delegate)
   : delegate_(delegate),
     window_(NULL),
     is_minimized_(false),
     bounds_updates_started_(false) {
  ui::ActiveWindowWatcherX::AddObserver(this);
}

PanelStackWindowGtk::~PanelStackWindowGtk() {
  ui::ActiveWindowWatcherX::RemoveObserver(this);
}

void PanelStackWindowGtk::Close() {
  if (!window_)
    return;
  gtk_widget_destroy(GTK_WIDGET(window_));
  window_ = NULL;
}

void PanelStackWindowGtk::AddPanel(Panel* panel) {
  panels_.push_back(panel);

  EnsureWindowCreated();
  SetStackWindowBounds();

  // The panel being stacked should not appear on the taskbar.
  gtk_window_set_skip_taskbar_hint(panel->GetNativeWindow(), true);
}

void PanelStackWindowGtk::RemovePanel(Panel* panel) {
  panels_.remove(panel);

  SetStackWindowBounds();

  // The panel being unstacked should re-appear on the taskbar.
  // Note that the underlying gtk window is gone when the panel is being
  // closed.
  GtkWindow* gtk_window = panel->GetNativeWindow();
  if (gtk_window)
    gtk_window_set_skip_taskbar_hint(gtk_window, false);
}

void PanelStackWindowGtk::MergeWith(NativePanelStackWindow* another) {
  PanelStackWindowGtk* another_stack =
      static_cast<PanelStackWindowGtk*>(another);
  for (Panels::const_iterator iter = another_stack->panels_.begin();
       iter != another_stack->panels_.end(); ++iter) {
    Panel* panel = *iter;
    panels_.push_back(panel);
  }
  another_stack->panels_.clear();

  SetStackWindowBounds();
}

bool PanelStackWindowGtk::IsEmpty() const {
  return panels_.empty();
}

bool PanelStackWindowGtk::HasPanel(Panel* panel) const {
  return std::find(panels_.begin(), panels_.end(), panel) != panels_.end();
}

void PanelStackWindowGtk::MovePanelsBy(const gfx::Vector2d& delta) {
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); ++iter) {
    Panel* panel = *iter;
    gfx::Rect bounds = panel->GetBounds();
    bounds.Offset(delta);
    panel->SetPanelBoundsInstantly(bounds);
  }

  SetStackWindowBounds();
}

void PanelStackWindowGtk::BeginBatchUpdatePanelBounds(bool animate) {
  // Bounds animation is not supported on GTK.
  bounds_updates_started_ = true;
}

void PanelStackWindowGtk::AddPanelBoundsForBatchUpdate(
    Panel* panel, const gfx::Rect& new_bounds) {
  DCHECK(bounds_updates_started_);

  // No need to track it if no change is needed.
  if (panel->GetBounds() == new_bounds)
    return;

  // New bounds are stored as the map value.
  bounds_updates_[panel] = new_bounds;
}

void PanelStackWindowGtk::EndBatchUpdatePanelBounds() {
  DCHECK(bounds_updates_started_);

  bounds_updates_started_ = false;

  for (BoundsUpdates::const_iterator iter = bounds_updates_.begin();
       iter != bounds_updates_.end(); ++iter) {
    iter->first->SetPanelBoundsInstantly(iter->second);
  }
  bounds_updates_.clear();

  SetStackWindowBounds();

  delegate_->PanelBoundsBatchUpdateCompleted();
}

bool PanelStackWindowGtk::IsAnimatingPanelBounds() const {
  return bounds_updates_started_;
}

void PanelStackWindowGtk::Minimize() {
  gtk_window_iconify(window_);
}

bool PanelStackWindowGtk::IsMinimized() const {
  return is_minimized_;
}

void PanelStackWindowGtk::DrawSystemAttention(bool draw_attention) {
  gtk_window_set_urgency_hint(window_, draw_attention);
}

void PanelStackWindowGtk::OnPanelActivated(Panel* panel) {
  // If a panel in a stack is activated, make sure all other panels in the stack
  // are brought to the top in the z-order.
  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); ++iter) {
    GtkWindow* gtk_window = (*iter)->GetNativeWindow();
    if (gtk_window) {
      GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(gtk_window));
      gdk_window_raise(gdk_window);
    }
  }
}

void PanelStackWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
  // Bail out if icewm is detected. This is because icewm always creates a
  // window as active and we do not want to perform the logic here to
  // activate a panel window when the background window is being created.
  if (ui::GuessWindowManager() == ui::WM_ICE_WM)
    return;

  if (!window_ || panels_.empty())
    return;

  // The background stack window is activated when its taskbar icon is clicked.
  // When this occurs, we need to activate the most recently active panel.
  if (gtk_widget_get_window(GTK_WIDGET(window_)) == active_window) {
    Panel* panel_to_focus =
        panels_.front()->stack()->most_recently_active_panel();
    if (panel_to_focus)
      panel_to_focus->Activate();
  }
}

gboolean PanelStackWindowGtk::OnWindowDeleteEvent(GtkWidget* widget,
                                                  GdkEvent* event) {
  DCHECK(!panels_.empty());

  // Make a copy since closing a panel could modify the list.
  Panels panels_copy = panels_;
  for (Panels::const_iterator iter = panels_copy.begin();
       iter != panels_copy.end(); ++iter) {
    (*iter)->Close();
  }

  // Return true to prevent the gtk window from being destroyed.  Close will
  // destroy it for us.
  return TRUE;
}

gboolean PanelStackWindowGtk::OnWindowState(GtkWidget* widget,
                                            GdkEventWindowState* event) {
  bool is_minimized = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED;
  if (is_minimized_ == is_minimized)
    return FALSE;
  is_minimized_ = is_minimized;

  for (Panels::const_iterator iter = panels_.begin();
       iter != panels_.end(); ++iter) {
    GtkWindow* gtk_window = (*iter)->GetNativeWindow();
    if (is_minimized_)
      gtk_window_iconify(gtk_window);
    else
      gtk_window_deiconify(gtk_window);
  }

  return FALSE;
}

void PanelStackWindowGtk::EnsureWindowCreated() {
  if (window_)
    return;

  DCHECK(!panels_.empty());
  Panel* panel = panels_.front();

  // Create a small window that stays behinds the panels.
  window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
  gtk_window_set_decorated(window_, false);
  gtk_window_set_resizable(window_, false);
  gtk_window_set_focus_on_map(window_, false);
  gtk_widget_show(GTK_WIDGET(window_));
  gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)),
      panel->GetBounds().x(), panel->GetBounds().y(), 1, 1);
  gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));

  // Connect signal handlers to the window.
  g_signal_connect(window_, "delete-event",
                   G_CALLBACK(OnWindowDeleteEventThunk), this);
  g_signal_connect(window_, "window-state-event",
                   G_CALLBACK(OnWindowStateThunk), this);

  // Should appear on the taskbar.
  gtk_window_set_skip_taskbar_hint(window_, false);

  // Set the window icon and title.
  base::string16 title = delegate_->GetTitle();
  gtk_window_set_title(window_, base::UTF16ToUTF8(title).c_str());

  gfx::Image app_icon = delegate_->GetIcon();
  if (!app_icon.IsEmpty())
    gtk_window_set_icon(window_, app_icon.ToGdkPixbuf());
}

void PanelStackWindowGtk::SetStackWindowBounds() {
  if (panels_.empty())
    return;
  Panel* panel = panels_.front();
  // Position the small background window a bit away from the left-top corner
  // such that it will be completely invisible.
  gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)),
      panel->GetBounds().x() + 5, panel->GetBounds().y() + 5, 1, 1);
}

/* [<][>][^][v][top][bottom][index][help] */